Friday, August 28, 2026
55 changes · saas-19.4
Resolved issues and error corrections
The point of sale refund screen now ignores barcode scanner input when staff are entering refund quantities. This prevents scanned product codes from being mistaken for refund amounts, avoiding incorrect refund quantities and confusing error messages.
Original PR description
Steps to reproduce: - Have a paid order with a product ordered once - Open the ticket screen, select that order and its line - Scan a product barcode with a keyboard-wedge scanner Issue: "Maximum…
Steps to reproduce: - Have a paid order with a product ordered once - Open the ticket screen, select that order and its line - Scan a product barcode with a keyboard-wedge scanner Issue: "Maximum Exceeded - The requested quantity to be refunded is higher than the ordered quantity. 6 is requested while only 1 can be refunded." When the line holds enough quantity no dialog is shown at all and a refund quantity taken from the barcode is silently set. Cause: A keyboard-wedge scanner types the barcode as a burst of keystrokes. The number buffer discards such bursts by waiting barcodeService.maxTimeBetweenKeysInMs before handling the keys it collected and dropping any batch of more than two, but only when its holder asks for it with `useWithBarcode`. TicketScreen never set the flag, so its buffer handled every keystroke on its own and the digits of the barcode reached _setToRefundDetail as the refund quantity. ProductScreen, OrderSummary and PaymentScreen all set it. Fix: Set `useWithBarcode: true` on the ticket screen number buffer. Since the keys are now handled with a delay, capture the buffer before the selected order or orderline changes, so that a keystroke is applied to the line that was selected when it was typed and not to the next one. opw-6465148 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283993 Forward-Port-Of: odoo/odoo#281962
Tax returns will no longer be incorrectly marked as Paid when users reply to or send normal chatter messages. Payment finalization now only happens from the intended tax payment instructions flow, reducing accidental status changes and improving reporting accuracy.
Original PR description
Before this fix: Replying to or sending a message from the chatter of a tax return could incorrectly change its state to Paid. This happened because action_send_mail() automatically called _action_finalize_payment() for account.return records. After this fix: Payment finalization only happens when the composer is opened from the tax payment instructions flow. Normal chatter messages and replies will no longer change the tax return state to Paid. task-6469536 Forward-Port-Of: odoo/enterprise#129161
This fixes an error that prevented users from printing sales timesheet reports when using German. The report now identifies the Description column in a language-independent way, so translated labels no longer break printing.
Original PR description
Currently a traceback is occurring when the user tries to print a sale timesheet report in the German language. **<h3>To reproduce the issue:</h3>** 1) Install the `sale_timesheet` module. 2) Create…
Currently a traceback is occurring when the user tries to print a sale timesheet report in the German language. **<h3>To reproduce the issue:</h3>** 1) Install the `sale_timesheet` module. 2) Create a `service` product configured to create a `project and task` on the order. 3) Create a confirmed SO with that product 4) Add a timesheet line to the SO from the `Recorded` stat button 5) Switch to the German language 6) Print `Timesheet(Zeiterfassung)` report 7) A traceback occurs **<h3>Error:</h3>** ``` ValueError: Element „<xpath expr="//th/span[text()='Description']">“ kann nicht in der übergeordneten Ansicht lokalisiert werden ``` **<h3>Cause:</h3>** The xpath relies on the plain text `Description` to identify the `<th>` element. https://github.com/odoo/odoo/blob/5f63fb1af418cc75ff2e382b0455002e7a798071/addons/sale_timesheet/report/report_timesheet_templates.xml#L3-L4 The xpath cannot find the Description header when the language is changed because the text is translated in the base template. As a result, the xpath fails due to the missing target. **<h3>Fix:</h3>** Use a language-independent `name` attribute as the `xpath` anchor. So the `Description` column can be reliably matched regardless of the active language. **<h3> Note:</h3>** This fix requires both modules update, which is generally risky in stable branches. However, this template was recently introduced in [saas-19.4](https://github.com/odoo/odoo/pull/191969/changes#diff-9f4f0d28ffb3aa9fcc4a5f1037bdf532e1e71c4e9703e13b6856ddfd79e15e69R4). So there should be no existing customers using it yet, except saas customers coming from a new database. Therefore, applying this fix in 19.4 is less risky. opw- 6475623
This update prevents PDF Quote generation from failing when Quote Builder documents include dynamic fields. It improves reliability for sales teams using quote templates, especially in newer Python and PDF library environments.
Original PR description
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1-…
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1- Using a python 3.13 env, install requirements.txt. (You could instead uninstall pypdf2 and install pypdf==5.4.0) 2- Enable Quote Builder. 3- Create a SO and in quote builder tab, select a document. This document should have dynamic fields. e.g. you could use`Office Furnitures Header` document. 4- Print -> PDF Quote. Cause: --- In previous fix, we fixed the traceback when no dynamic field is set. However, if you have a dynamic field, then inside `PdfWriter._update_field_annotation()`, the font is get from `DR` dict inside acroform: https://github.com/py-pdf/pypdf/blob/f20954f2241640feb484800e191373f8fbdfa44b/pypdf/_writer.py#L917-L929 Even if we are not setting a font, we need to have an empty `DR` dict inside acroform, in order to avoid calling `get` on a none object, which is leading to the traceback. Also in previous fix we were losing `NeedAppearances` inside `AcroForm` by creating new dict which wasn't right. opw-6392001 Forward-Port-Of: odoo/odoo#278881
This update fixes an internal mail test that could fail unpredictably when checking mention suggestions. It makes the test focus only on the relevant suggestion list, improving confidence in automated checks without changing user-facing behavior.
Original PR description
Before this commit, the test "select @ mention from the suggestion list being filtered" could fail on runbot, on the check that follows the first "@": Failed to find 2 of ".o-mail-Composer-suggestion" (Timeout of 10 seconds). Found 0 instead. This happens because the test holds a render open on ImStatus, a component the member list renders as well as the composer. The composer tells the server that the user is typing, the bus sends the status back, and the member list re-renders its ImStatus with another class. The hold catches that render, the one that also brings the suggestions on screen. This commit gives the children of NavigableList an inNavigableList environment flag, and holds the render only on an ImStatus that has it. https://runbot.odoo.com/odoo/error/946282 Forward-Port-Of: odoo/odoo#285093 Forward-Port-Of: odoo/odoo#284484
This fixes an issue where checks created from check templates no longer appeared on matching tax returns. Accounting users can again see the expected compliance checks when generating and reviewing tax returns.
Original PR description
commit introducing the issue: https://github.com/odoo/enterprise/commit/034e0157eed0f19471879ca133faa40b7f0aabf3 Since this commit, it's no longer possible to see a check defined from a check template in a tax return. Steps to reproduce: - Go to Accounting / Configuration / Checks - Create a new one, give it a name, a random cycle and assign it a to a tax return - Open the tax returns view, generate them, and open a tax return of the same type -> The check should be visible Forward-Port-Of: odoo/enterprise#129707
Fixes a display issue in the website appointment editor where a selection button could appear squeezed and a loading indicator could remain visible. This makes appointment page editing clearer and more consistent for website administrators.
Original PR description
Introduced in [1], and in a series of related changes, the time selection page has been reworked to be managed more from the JS, using the Interaction benefits. The entity selection is now a custom dropdown, handled in the JS. When editing the page with the website editor, the button is squashed, because no content is rendered inside. This is not the case for other ones as either a select element, either have a t-out dynamic content that will still instanciate some content at loading. Therefore, instead of meddling with a complex JS, add some simple styling to make sure the element has a consistent height. Also, hide the loader when editing the page in a similar way. [1] odoo/enterprise@a5712d284a87af7530f186ecad765d2cfaf1f9d2 Task-6482389
New onsite learning events created from the Onsite view or an employee resume now remain visible immediately after they are saved. This prevents users from thinking their newly created training events disappeared and ensures the current employee is registered automatically where needed.
Original PR description
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the…
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the event to have multiple slots as well as to have at least one employee registered to it. Therefore, newly created records often fail these criteria and remain hidden. In further versions, this pr: https://github.com/odoo/odoo/pull/246285/ changes the domain of the event selector in the employee resume by removing the dependency on the multiple slots and filtering by the specific employee for registration. This change is not stable to backport as it indroduces the `employee_id` field as an invisible field in the xml to be able to compare in the domain. This commit partly changes both domains to not require the multiple slots anymore, while still showing all events for which an employee is registered. This commit also ensures that when an event is created from the Onsite view or selector, the current user's employee will be registered to it. Steps to reproduce - Go to employees->Learning->Onsite - Select New and create an event - Go back to Onsite Courses - You will not see the created event (unless it is multi_slot and an employee was registered) opw-5915686 Forward-Port-Of: odoo/odoo#284200 Forward-Port-Of: odoo/odoo#258952
This fix prevents Odoo Inventory from replacing a delivery operation's custom customer destination location with the generic Customers location when a contact is selected. Businesses using specific customer stock locations keep the intended transfer destination, reducing manual corrections and delivery errors.
Original PR description
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver…
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver Super Customer" and set its default destination location to "Customer stock" - Go to Inventory > Overview > Deliver Super Customer > New - Set a contact on the transfer #### > The destination location switches from "Customer stock" to "Customers" ### Cause of the issue: The `location_dest_id` of `stock.picking` depends on its `partner_id`. So that changing the partner recomputes the locations of the transfer. However, as soon as the destination of the operation type has a `customer` usage, the `property_stock_customer` of the contact replaces it unconditionally: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L949-L963 However, the `property_stock_customer` falls back to an `ir.default` pointing at the default `Customers` location when nothing is set on the contact: https://github.com/odoo/odoo/blob/1c40fab04b71def8f3645c4c4bb0c1441057f307/addons/stock/data/stock_data.xml#L71-L72vs The override comes from 8a0775aa1dd9, which replaced an `elif` fallback on the contact by an "unconditional" substitution as this fallback had become unreachable once `default_location_src_id` and `default_location_dest_id` were made required: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L34-L41 opw-6421090 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283886 Forward-Port-Of: odoo/odoo#280016
The Real Margin report now keeps the expected list views when users drill down from the pivot table. This prevents users from being sent to the timesheet list by mistake and makes margin details easier to review from projects.
Original PR description
**Steps to reproduce:** 1. Open a project. 2. Click on the Real Margin stat button or top menu action. 3. The pivot view opens by default. 4. Click on a cell in the pivot view to drill down into the list view. **Issue:** The system opens the timesheet list view instead of the standard analytic entries list view. **Cause:** Overwriting action['views'] erased the default list view, causing to fall back to the timesheet view during drill-down. **Fix:** Used a list comprehension to inject the custom pivot view while preserving the original view types. Added all view options to the Real Margin top bar. task-6192267 Forward-Port-Of: odoo/odoo#270494
This fixes an issue where confirming a sales order again after cancellation could fail to create the expected event registration. Businesses can now rely on event attendee records being recreated correctly when an order is returned to quotation and confirmed again, including portal confirmation flows.
Original PR description
**Steps to reproduce:** - Create a SO and add the Event Registration - Standard product and specify an event - Confirm the SO then select "Create/Update registrations" - Cancel the SO, then "Set to…
**Steps to reproduce:** - Create a SO and add the Event Registration - Standard product and specify an event - Confirm the SO then select "Create/Update registrations" - Cancel the SO, then "Set to Quotation" - Select "Preview" and confirm the Sale Order again - There will not be any new registration created when there should be one. **Behavior:** Usually when a sale order is confirmed the `action_sale_order_event_registration` form will be opened which when filled correctly creates registrations. However in certain cases: confirming from the customer portal, or simply closing the form when it is opened, will not trigger `action_make_registration` which creates registrations if it is not already the case `action_confirm()` should be creating the registrations correctly on its own anyway by calling ´init_registrations()´ : https://github.com/odoo/odoo/blob/beed378cde592bc96c1e79a976ac775264b843ed/addons/event_sale/models/sale_order_line.py#L49-L66 This function tries to create each missing registrations by looking at the amount in the so_line and deducting the already created registrations, however since some of them can be cancelled, this computation is wrong. And leads to registration not being created when they should. opw-6444127 Forward-Port-Of: odoo/odoo#280426
This fixes an issue in Documents where clicking inside the "Search More..." selection window could unexpectedly close it. Users can now sort, resize columns, and select contacts or other related records from the popup without losing their current document selection.
Original PR description
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the…
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the field dropdown, click "Search More..." to open a modal dialog. 5. Click inside the "Search More..." modal (e.g., to sort columns or resize headers). Issue: - The modal dialog immediately closes, and the contact cannot be selected. Root cause: - When an inspector field is edited, the record row is put into edit mode. While in edit mode, the documents list renderer listens for global clicks. Clicking inside the "Search More..." modal dialog targets elements that have `.o_list_renderer` (since the modal dialog renders a list view). Because the click target is within a list renderer but is not a document row, `DocumentsListRenderer.onGlobalClick` executes and clears the selection of the main list view. Clearing the selection unmounts the edited field in the inspector, thereby destroying the modal dialog stack. Solution: - Modify DocumentsListRenderer.onGlobalClick to scope click handling to the current Documents list renderer. Ignore clicks outside this.root.el, so interactions in nested UI such as Search More... do not clear the main selection and destroy the inspector field. opw-6253360 Forward-Port-Of: odoo/enterprise#128812 Forward-Port-Of: odoo/enterprise#119262
The project profitability grid view has been corrected so the real margin information in the top bar displays as intended. This helps users review project financial performance more clearly and consistently.
Original PR description
- update the grid view in project real margin top bar task-6192267 Forward-Port-Of: odoo/enterprise#122586
French VAT report submissions now automatically split account holder names that exceed the official XML-EDI length limit. This helps prevent rejected electronic VAT filings when company or account holder names are longer than allowed.
Original PR description
The XSD for XML-EDI does not allow strings longer than 35 for TitulaireDesignation This commit splits the holder name in 2 parts when it is more than 35 characters task-6476440 Forward-Port-Of: odoo/enterprise#129411 Forward-Port-Of: odoo/enterprise#128239
The project form settings layout has been corrected so related section headers line up consistently. Setting descriptions now use the available horizontal space better, making the page easier to scan and reducing unnecessary line wrapping.
Original PR description
In the project form settings: - The sections that sit on the same horizontal level should have their header aligned. - Settings description should use all the horizontal space available before wrapping to the next line Task-6360046
Fixed an issue where confirmed manufacturing orders in warehouses using a 3-step process were not counted in stock forecasts. This helps planners see incoming finished goods correctly and avoid unnecessary replenishment or purchasing decisions.
Original PR description
### Steps to reproduce: - In the settings enable Multi-Steps Routes - Put your warehouse in manufacture in 3 steps - Create a storable product P - Create and confirm an MO for 1 unit of P - Go to…
### Steps to reproduce: - In the settings enable Multi-Steps Routes - Put your warehouse in manufacture in 3 steps - Create a storable product P - Create and confirm an MO for 1 unit of P - Go to Inventory > Operations > Procurement > Replenishment - Create a new one for P in WH/stock #### > The forecasted quantity in stock is still 0 but should be at 1 ### Cause of the issue: This is the exact use case already fixed in 85dd3369ed17b98b2ce485be04f140cf4cfa8aa3, which stamped the finished move with a `location_final_id` pointing at WH/Stock so that the move contributes to the forecast there even though its `location_dest_id` is the intermediate WH/Post-Production. That fix was reverted in practice by 42275f83dc5350822a625e19d65148e8b41ab1d4, which replaced the value with `mo.location_dest_id`: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/mrp/models/stock_move.py#L466-L467 Its reasoning was that in a single-warehouse setup `location_dest_id` equals the warehouse stock location, so the behaviour would be unchanged. That holds in 1 and 2 steps, where the extra step is on the component side and only moves `default_location_src_id` to the pre-production location. It breaks in 3 steps, the only mode that also moves `default_location_dest_id`, to the post-production location: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/mrp/models/stock_warehouse.py#L246-L247 and `_compute_locations` propagates it to the MO: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/mrp/models/mrp_production.py#L334-L341 WH/Post-Production is a sibling of WH/Stock under the warehouse view location, not a child of it. Since `location_final_id` takes precedence over `location_dest_id` for the non-done part of the move chain: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/stock/models/product.py#L331-L334 the finished move stopped being counted in the WH/Stock forecast. Why the test did not catch it: `test_3_steps_manufacturing_forecast` stayed green through the whole regression, because it scoped `virtual_available` with a `location_id` context key. `_get_domain_locations` only reads `location` and `warehouse_id`; `location_id` is silently ignored: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/stock/models/product.py#L284-L287 The call therefore fell through to the branch scoping the forecast to every warehouse view location: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/stock/models/product.py#L303-L309 and the warehouse view location is the common parent of both WH/Stock and WH/Post-Production. The assertion held regardless of where `location_final_id` pointed, so the test was a false positive from the start: it also passes with 85dd3369ed17b98b2ce485be04f140cf4cfa8aa3 fully reverted. Using the `location` key makes it fail without the fix and pass with it. ### Fix: Neither fix proposition was right on its own; each one was correct only in its own scenario. The`mo.warehouse_id.lot_stock_id` resolves the warehouse from the components, so it points at the wrong warehouse as soon as the finished product is produced for another one. `mo.location_dest_id` is the post-production location as soon as the warehouse manufactures in 3 steps, so it drops the quantity from the forecast of the manufacturing warehouse itself. What separates the two is not the warehouse but whether the destination is a transit step. In 3 steps the finished product only reaches the stock through the post-production push rule: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/mrp/models/stock_warehouse.py#L57 so the final location is the stock of the warehouse owning that destination. Any other destination is already final and is kept as is, which leaves cross-warehouse MOs and destinations set to a sub-location of the stock untouched. The rule's destination is read rather than `warehouse.lot_stock_id` because a push move takes its destination from the rule and not from the operation type: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/stock/models/stock_rule.py#L256-L260 so the forecast stays correct when the store step is reconfigured to land somewhere else than the warehouse stock. The rule is looked up on `pbm_route_id` by its `picking_type_id` instead of through `warehouse.sam_rule_id`, because that field is no longer set. It used to be an entry of `_generate_global_route_rules_values`, and it is that entry which made the generic warehouse machinery create the rule and store it back on the warehouse: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/stock/models/stock_warehouse.py#L403-L410 11e69870db1c49d9a6af79ffd263e4e162b34b6b removed it when the post-production step stopped being a pull rule on the Manufacture route and became a push rule generated from `get_rules_dict`. Only the field declaration was left behind, and nothing writes it any more: https://github.com/odoo/odoo/blob/6a56908e5febfdb4e6e0eacfb8655b092319819f/addons/mrp/models/stock_warehouse.py#L21-L22 so reading it would silently give an empty recordset. The lookup is not delegated to `_get_push_rule` to avoid a search per finished move. opw-4882390 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283952 Forward-Port-Of: odoo/odoo#283234
Fixed an issue where AI-related dialogs, popovers, and other overlays could appear behind the Mass Mailing fullscreen editor. This keeps AI tools usable while editing mailings in fullscreen mode.
Original PR description
When using AI features from the Mass Mailing fullscreen editor, dialogs, popovers, and other overlays are displayed behind the editor, making them unusable. This regression is caused by the AI module overriding the z-index of the shared overlay classes with values lower than those required by the Mass Mailing fullscreen editor. Since the AI assets are loaded after the Mass Mailing assets, their CSS rules take precedence, causing all overlays using these shared classes to be rendered below the fullscreen editor. Raise the AI z-index values from the sticky layer to the offcanvas layer so the shared overlay classes preserve the correct stacking order when the Mass Mailing editor is displayed in fullscreen mode. task-6412411
Self-order preparation receipts now include the customer name when it was provided with the order. This helps restaurant staff identify orders more easily and reduces confusion during preparation and handoff.
Original PR description
The customer name is written in `floating_order_name` which is never passed to the preparation receipt in self order. This commit fixes it. Forward-Port-Of: odoo/odoo#284454
Payment XML files for SEPA and ISO 20022 now use uppercase encoding names to satisfy stricter bank validation checks. This reduces the risk of warnings or rejected payment files from providers such as SIX in Switzerland.
Original PR description
The W3C recommendations for XML state that the encoding defined for an XML document should not be case-sensitive. However, some banking providers (SIX for Switzerland) are stricter and may throw warnings or errors if upper-case is not used. https://www.w3.org/TR/2008/REC-xml-20081126/#NT-EncodingDecl opw-4948708 Forward-Port-Of: odoo/enterprise#128517 Forward-Port-Of: odoo/enterprise#125807
This update prevents an internal social CRM test from failing because of duplicate demo customer names. It uses a unique test customer name so results no longer depend on which demo data or modules are installed first.
Original PR description
The social CRM conversion test creates a partner named "John Doe" and expects the post-to-lead wizard to automatically match it. This relies on "John Doe" being unique in the database. Since `pos_restaurant.customer_1` is also named "John Doe", the wizard's `name_search()` can return multiple partners depending on the modules already installed when the test is run. In that case, the wizard correctly considers the match ambiguous and leaves `partner_id` empty, causing the test to fail. This commit uses a test-specific author name instead, ensuring that the test actually provides the single matching partner described by its docstring and does not depend on unrelated demo data or module installation order. [error-243065](https://runbot.odoo.com/odoo/error/243065) Forward-Port-Of: odoo/enterprise#128119
The inventory report now prints location grouping rows with the correct number of columns. This prevents missing gridlines and broken borders in the PDF, making printed inventory counts easier to read and use.
Original PR description
When new columns were added to the stock inventory report, the location grouping row was not updated. This results in mismatched column counts, causing missing gridlines and broken borders in the PDF output Fixed by ensuring the location row's column count matches the header <img width="603" height="200" alt="image" src="https://github.com/user-attachments/assets/86872bee-f315-4bdd-b3f2-a525e3bb5fe0" /> ### Steps to reproduce: - Ensure warehouses are activated in the settings - Go to Barcode -> Count Inventory - Add a Product - Select the gear Icon then "Print Inventory" - You will notice that the location row has missing gridlines opw-6307728 Forward-Port-Of: odoo/odoo#275918
Standard timesheet users can now access Assistant Rules when the assistant feature is enabled, even if Billing Rate Indicators are disabled. This prevents users from needing debug mode or administrator rights to reach configuration options required for their work.
Original PR description
Steps to reproduce --- 1. Install sale_timesheet_enterprise. 2. Go to Timesheets > Configuration > Settings (as Admin). 3. Disable the "Billing Rate Indicators" setting and enable timesheet…
Steps to reproduce --- 1. Install sale_timesheet_enterprise. 2. Go to Timesheets > Configuration > Settings (as Admin). 3. Disable the "Billing Rate Indicators" setting and enable timesheet assistant. 4. Log in as a normal user with "Timesheets > Own timesheet" access. 5. Open the Timesheets app. Issue --- The "Configuration" menu is completely hidden. The regular user cannot access the "Assistant Rules" menu unless they turn on debug mode. Cause --- When the billing rates feature is turned off, the _load_menus_blacklist function hides the Enterprise Configuration menu. The logic used an and condition, meaning the menu was only kept visible if the user was an Assistant AND had Manager/Admin rights. This locked out standard users. Fix --- Change the blacklist condition. Allow the Enterprise Configuration menu to stay visible if the user needs it for the Assistant feature (when UoM is not Days), OR if the user is a Manager/Admin. References to check other issues https://github.com/odoo/enterprise/pull/120609 https://github.com/odoo/enterprise/pull/117984 task - 6470183 Forward-Port-Of: odoo/enterprise#128096
This fixes an issue where manually adjusted prices on optional quotation items were overwritten when customers changed quantities in the portal preview. Sales teams can now rely on custom negotiated prices remaining unchanged unless intentionally updated.
Original PR description
When a user manually sets a price on an optional line (overriding the pricelist), and then changes the quantity in the portal preview, the manual price is lost and gets reset to the pricelist price.…
When a user manually sets a price on an optional line (overriding the pricelist), and then changes the quantity in the portal preview, the manual price is lost and gets reset to the pricelist price. Steps to reproduce: --- - Install Sales module and enable Pricelists. - Create a product with qty-based pricelist rules: - min qty: 1 → price: 100 - min qty: 10 → price: 80 - Create a quotation with an optional section containing this product. - Manually change the product price to 150 (overriding pricelist). - Mark the section as optional and preview the quotation. - Change the quantity to 10 in portal preview. Issue: --- - The manually set price (150) is incorrectly reset to the pricelist price (80). Root cause: --- - After [commit], if there is no config parameter set and if there is an active pricelist, we simply call `_reset_price_unit()` without checking whether the price was manually set or not. - Additionally, `_reset_price_unit()` calls `update()` which writes `price_unit` and `technical_price_unit` one by one as separate `write()` calls. The `write()` method has a guard([1]) that strips a lone `technical_price_unit` write unless `sale_write_from_compute` is set in context. Without this flag, `technical_price_unit` is silently discarded, causing it to drift from `price_unit`. On the next qty change, this mismatch is detected as a manual price, permanently blocking further pricelist updates. Solution: --- - Check whether the price was manually set before calling `_reset_price_unit()`, and pass `sale_write_from_compute=True` in context so both `price_unit` and `technical_price_unit` are written correctly. [commit]: https://github.com/odoo/odoo/commit/93b6bdd6a4909bc0b45b90ab6a2d0734a218292d [1]https://github.com/odoo/odoo/blob/fffd987cc98d1ea0cd04e24dda2ed8b64a219cdc/addons/sale/models/sale_order_line.py#L1391-L1399 opw-6426634 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281107
Fixes an issue in the website editor where changing a custom button's text color could remove its gradient background. This helps users keep their intended button styling while making design changes, reducing rework and visual inconsistencies on website pages.
Original PR description
Steps to Reproduce : 1. Go to Website → Edit Mode 2. Add a snippet with button 3. Click on button and change its type to : " Custom" 4. Apply the gradient type color in fill color option 5. Apply any…
Steps to Reproduce : 1. Go to Website → Edit Mode 2. Add a snippet with button 3. Click on button and change its type to : " Custom" 4. Apply the gradient type color in fill color option 5. Apply any color in text color option 6. You will notice that the gradient type color in fill color option is removed. Problem: Since [this commit][1] new button style options have been added to the sidebar. If one changes the style of a button to custom, changes the background to gradient, and tries to change the text color, the background gradient is removed. Cause: Whenever a gradient is added either to text or as a background, it is applied as a background image. In the case of a text gradient, an additional class, `text-gradient`, is applied for correct styling. Once a change to either color or background/fill is applied that is not a gradient color change, the background image of the element would be reset to nothing. This is the result of [this line][2] from a [previous commit][3]. In the case of a button, this meant changing the font color would reset the background. That is not the desired outcome. The flaw was only discovered once new options were added to change non-text/font elements' backgrounds to gradient. Solution: An additional check has been added to see if the element being edited is text. If so, and it's a gradient style being changed, we remove the background image. Otherwise it is kept so that the button case from above is resolved. [1]: https://github.com/odoo/odoo/commit/2bf1db001b195480b963b338583c170aa1c009a3 [2]: https://github.com/odoo/odoo/blob/8e0845712f462ecfafd2176406dcbafc869a5564/addons/html_editor/static/src/main/font/color_plugin.js#L575 [3]: https://github.com/odoo/odoo/commit/8e0845712f462ecfafd2176406dcbafc869a5564 task-6247134 Forward-Port-Of: odoo/odoo#284788 Forward-Port-Of: odoo/odoo#279070
Closed or renewed subscriptions will no longer appear in Orders to Invoice just because prepaid recurring lines were still marked for billing. This prevents misleading invoice prompts while still allowing billing for postpaid items that were already delivered before closure.
Original PR description
When a closed subscription could still show up in the Orders to Invoice because its recurring lines kept their "to invoice" status. This was misleading since no further period should be billed. When a subscription is churned (or renewed), prepaid lines are now flagged as nothing to invoice. Postpaid lines are left as is so that already delivered products can still be billed after closing. task-6227787 Forward-Port-Of: odoo/enterprise#117797
Customers can no longer set optional products on sales orders to negative quantities through the portal. This prevents confusing or invalid order changes and keeps exceptional quantity adjustments under salesperson control.
Original PR description
Since the fusion of `sale.order.option` model into `sale.order.line` model, the optional products (editable from portal) lines are not deleted when reaching a quantity of 0 or below. This could allow some customers to set negative quantities, which makes no sense as it's only something that should be set by the salesman if necessary. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284493
Belgian employee departures now avoid unintended archiving when no archive date is set. Future leave requests are refused rather than deleted, and company car assignments remain recorded, preserving important HR history and reducing accidental data loss.
Original PR description
Before: * Belgian employees without an "Archive Employee On" date were automatically archived after their departure. * Future leaves were deleted when applying an employee departure. * The employee's company car was automatically unassigned on departure. After: * Belgian employees without an "Archive Employee On" date are no longer automatically archived. * Future leaves are refused instead of deleted. * Company car assignments are kept when applying a Belgian employee departure. * Display "Don't archive" when no archive date is set. Impact: * Prevents unintended archiving and preserves future leave and company car information for Belgian employees. Task: 6453718 Forward-Port-Of: odoo/enterprise#127414
Belgian payroll now correctly applies an employee's default private fuel card use to payslips when eligible. This ensures the related benefit in kind is categorized properly for social security and withholding calculations, reducing payroll calculation errors.
Original PR description
FUEL_CARD_PRIV never fired: nothing copied the employee's fuel_card_personal_use default into the payslip's property input. Fixed by seeding it in _compute_input_line_ids(), gated on fuel_card set, no company car, no mobility budget. FUEL_CARD_PRIV is also a Benefit in Kind (ONSS + withholding), like ATN.INT, but was missing the BIK category. Added it. Task 6469003 Forward-Port-Of: odoo/enterprise#128789
Submitting a tax report opened from a return now targets the exact return shown on screen, including Dutch VAT corrections. This prevents users from accidentally submitting or marking the original return instead of the intended correction, improving compliance accuracy.
Original PR description
Opening a tax report from a return and submitting it could act on a different return than the one on screen. _get_return_from_report_options searches by company, period and report with limit=1, but…
Opening a tax report from a return and submitting it could act on a different return than the one on screen. _get_return_from_report_options searches by company, period and report with limit=1, but that combination is not unique: l10n_nl declares two return types on l10n_nl.tax_report, nl_tax_return_type and nl_tax_correction_return_type, so a VAT return and its correction both match. Which one is returned is then decided by _order (is_completed, date_deadline, name, id). For a Dutch VAT correction it resolves to the original VAT return of the same quarter, so send_xbrl submits and flags that record instead of the correction. The options already carry the return type they were built for, in the return_periodicity filter, so restrict the search to it when it is set. l10n_nl_reports kept a return_id option for the same reason when computing the already declared amount of a suppletie; it can use _get_return_from_report_options now. opw-6421300 Forward-Port-Of: odoo/enterprise#129278 Forward-Port-Of: odoo/enterprise#127073
Employee departures no longer have to erase future leave requests when local rules require those records to remain available. Approved leave that overlaps the departure date is still cancelled, while confirmed future leave can be refused instead of deleted, preserving history without changing the default behavior for other cases.
Original PR description
Before: * Future leaves were deleted when an employee departure was applied. * There was no way for localizations to preserve these leaves when they should remain in the employee's history. After: * Add `refuse_future_leaves` to `_cleanup_employee_departure_leaves()`. * Approved leaves are still cancelled when they extend beyond the departure date. * Future confirmed leaves can now be refused instead of deleted when requested by a localization. * Keep the existing deletion behavior by default for other localizations. Impact: * Allows localizations to preserve future leave records while keeping the existing generic departure behavior unchanged. Task: 6453718 Forward-Port-Of: odoo/odoo#281493
This fixes an issue where choosing “Do not ask me again” for IoT Box printing caused the next report to download as a PDF instead of printing. Users can now rely on their saved preference to send documents directly to the intended printer.
Original PR description
Since odoo/enterprise#113128, when a user prints a report through the IoT Box and enables the "Do not ask me again" checkbox, the next print: - before: it downloads the pdf instead of printing it, - after: it correctly prints the document. Forward-Port-Of: odoo/enterprise#129658
Copying an image that is already attached to another record now reuses the existing file instead of leaving an unnecessary duplicate. This helps keep stored media cleaner and avoids redundant attachments without changing the user workflow.
Original PR description
Copying an image attachment already linked to another record could leave a redundant duplicate behind instead of reusing the existing one. opw-6463012 Forward-Port-Of: odoo/odoo#284610 Forward-Port-Of: odoo/odoo#282287
Employee planning notification emails now show action buttons with a visible background, so links like "View your planning" can be read and clicked. This prevents confusion when employees receive published shifts or schedules.
Original PR description
Before this change: When publishing a shift or schedule, the buttons "Assign me this shift", "I am unavailable", and "View your planning" inside the notification email sent to the employee appears invisible. The button text is rendered in white on a white background, making the link unreadable and difficult to click. To reproduce: 1. Open the Planning app and create a shift with today's date in the time range. 2. Click "Publish". 3. Go to Settings > Technical > Email > Emails. 4. Open the email that was just sent. 5. Inspect the email body and observe that the "View your planning" button text is not visible. After this change: A default purple background is applied to the button, ensuring the white text is properly visible and legible across email clients. opw-6483147
Fixes an accounting issue where undoing a payment could place the reversing cash basis tax entry in the current period instead of the original tax period. This keeps tax reports balanced in the intended period, avoiding misleading amounts across reporting months.
Original PR description
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax…
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax report nets to zero for that period. Steps to reproduce: - Enable cash basis and create a cash basis tax (exigibility on payment) - Post an invoice dated in the past with that tax - Reconcile a bank statement line to the invoice - Resequence the cash basis entry so the month is dropped from the name (CABA/08/2026/0001 -> CABA/2026/0001) - Unreconcile the statement line Issue: The reversal CABA entry created on the last unreconcile is dated today instead of the origin entry's month. In the tax report the original tax amount stays in the statement's month while the reversal amount appears in the current month, so the two no longer cancel out. Analysis: While under a monthly journal sequence a past date returns the last day of that month, under a yearly sequence a past date within the current year returns the latter between the move date and today, moving the reversal out of the origin period. opw-6301553 Forward-Port-Of: odoo/odoo#284840 Forward-Port-Of: odoo/odoo#281250
The accounting report for invoiced items not yet delivered now excludes delivery fee lines, since those charges are not physical items to deliver. This prevents misleading report entries and helps accounting teams focus only on products that still require delivery follow-up.
Original PR description
Issue: --- Delivery lines are included in `invoiced not delivered` report, which is wrong as delivery lines are not deliverable. Steps: 1- Create a SO with a good product and add a delivery line. Set the product line as delivered and create an invoice. 2- Open accounting, and from review tab, open `Invoiced not Delivered`. As you see, delivery lines are included in the report. Fix: --- On stable we could fix it inside `_get_accrual_domain` by checking if `delivery` is installed. On master we need to implement a solution to be able to differentiate the lines that won't be delivered. opw-6360894 Forward-Port-Of: odoo/enterprise#123517
This change makes an automated CRM forecast check wait until an opportunity is fully marked as won before moving on. It reduces random test failures in the validation environment, helping keep releases and quality checks more stable without changing user-facing CRM behavior.
Original PR description
The crm_forecast tour is red randomly on runbot on the Won banner step. We click the won button and go back directly, so the kanban can be loaded before the lead is won. Now we wait for the ribbon first. runbot-242139 Forward-Port-Of: odoo/odoo#284721
This update fixes an automated Knowledge app test that was failing when entering calendar information. It helps keep quality checks reliable without changing the user-facing behavior of the app.
Original PR description
[Related PR 1] modified `editSelectMenuInput` to use the standard 'edit' action instead of a custom action, and moved several tours off of the helper function, but missed the knowledge calendar tour. In combination with [Related PR 2] which changed the conditions of editing a select input to not include the intial 'click' action, causes this tour to now fail to properly input the text value. This commit fixes this issue by using the new standard approach with the 'edit' action. Since there are no tours which make use of `editSelectMenuInput`, it is also deprecated and to be removed in master. Related PR 1: https://github.com/odoo/odoo/pull/264913 Related PR 2: https://github.com/odoo/odoo/pull/266912 runbot-941336
This fixes a broken automated tour step in Knowledge where calendar-related text input could fail after recent changes. It also marks an unused helper as deprecated, reducing reliance on outdated testing utilities without affecting normal users.
Original PR description
[Related PR 1] modified `editSelectMenuInput` to use the standard 'edit' action instead of a custom action, and moved several tours off of the helper function, but missed the knowledge calendar tour. In combination with [Related PR 2] which changed the conditions of editing a select input to not include the intial 'click' action, causes this tour to now fail to properly input the text value. This commit fixes this issue by using the new standard approach with the 'edit' action. Since there are no tours which make use of `editSelectMenuInput`, it is also deprecated and to be removed in master. Related PR 1: https://github.com/odoo/odoo/pull/264913 Related PR 2: https://github.com/odoo/odoo/pull/266912 runbot-941336
Invoice sequence gap warnings now check each suffix-based sequence separately. This prevents invoices from being incorrectly marked as having missing numbers when similarly numbered invoices with different suffixes exist, improving accounting clarity and audit confidence.
Original PR description
### Issue: When moves share the same `sequence_number` in a journal but have different suffixes (e.g. `INV/2026/00010` and `INV/2026/00010A`), the `made_sequence_gap` flag was incorrectly set ###…
### Issue: When moves share the same `sequence_number` in a journal but have different suffixes (e.g. `INV/2026/00010` and `INV/2026/00010A`), the `made_sequence_gap` flag was incorrectly set ### Cause: `_update_sequence_made_gap`, introduced in commit https://github.com/odoo/odoo/commit/17893089e8b21c0ecab5e61ed8e2c33f3731b3ac selects the previous and next moves ordered by `sequence_number` without filtering by suffix This causes two issues: - Moves from different suffix sequences are used as neighbors, leading to incorrect gap detection - Duplicate `sequence_number` values across suffixes are not accounted for, so only one move is considered per number ### Steps to reproduce: - Install `account` - Post 12 invoices to get a sequence up to `INV/2026/00012` - Reset `INV/2026/00012` to draft, rename it to `INV/2026/00010A` - Reset `INV/2026/00010A` to draft, rename it to `INV/2026/00009A` and confirm Before the fix: `INV/2026/00011` is red Expected: `INV/2026/00011` should not be red because `INV/2026/00010` exists - Delete `INV/2026/00009` Before the fix: `INV/2026/00010` is red Expected: `INV/2026/00010` should be red (gap in no-suffix sequence) - Reset `INV/2026/00009A` to draft and confirm it again Before the fix: `INV/2026/00010` is not red Expected: `INV/2026/00010` should still be red (different suffix) ### Notes: Suffix changes are treated as distinct sequences following the same gap rules as any other sequence This was agreed with R&D — the gap flag is meant to signal inconsistencies within a sequence, not across suffixes opw-6454823 Forward-Port-Of: odoo/odoo#282503
This fix prevents an unexpected error from appearing when Odoo handles certain report actions. It improves reliability by using the correct record context, helping users avoid interruptions during normal workflows.
Original PR description
opw-6360013 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#274977
This fix ensures costs linked to projects are matched to the right sales order for reinvoicing, even when projects share accounting links or multiple analytic accounts are involved. This helps prevent missed reinvoiceable items and improves billing accuracy.
Original PR description
### Before this fix --- The `_get_so_mapping_from_project()` method returns a mapping where the key is the move line ID and the value is a `sale.order` record (or `None`). Because of the issues…
### Before this fix
---
The `_get_so_mapping_from_project()` method returns a mapping where the key is
the move line ID and the value is a `sale.order` record (or `None`).
Because of the issues described below, a valid `sale.order` could be available
for reinvoicing, but the corresponding move line might still not be mapped to
that sale order. As a result, the move line is not added to the reinvoiceable
sale order.
However, the implementation has two issues:
#### 1. Projects are overwritten when they share the same analytic account
`project_per_accounts` is built as a dictionary mapping an analytic account ID
to a single project. If multiple projects reference the same analytic account,
each new assignment replaces the previous one. As a result, only the last
project associated with a given analytic account is retained.
**Example:**
* Analytic Account **AA1** is linked to **Project A** and **Project B**.
* The dictionary becomes `{AA1: Project B}`.
* **Project A** is lost, even though it also references **AA1**.
**Steps to reproduce:**
1. Create an analytic account **AA1**.
2. Create **Project A** and **Project B**, both linked to **AA1**.
3. Create **Sale Order SO1** linked only to **Project A**.
4. Create a vendor bill (or expense) that generates an AML using **AA1** for a
product configured with **Reinvoice Costs = At Sales Price**.
5. Validate the document.
**Expected behavior:**
The product should be added to **SO1** for reinvoicing.
**Actual behavior:**
The move line is not mapped to **SO1**, so no sale order line is created.
#### 2. Previously found projects are overwritten during iteration
The `project` variable is reassigned on every iteration of the loop. After the
loop completes, it only contains the project (or lack of one) corresponding to
the last processed analytic account. This can cause valid projects found earlier
in the loop to be discarded.
**Example:**
* Move line has analytic accounts **AA1** and **AA2**.
* **AA1** maps to **Project A**.
* **AA2** has no linked project.
* After the loop, `project` is `None`, even though **Project A** was found.
**Steps to reproduce:**
1. Create analytic accounts **AA1** and **AA2**.
2. Create **Project A** linked to **AA1** only.
3. Create **Sale Order SO1** linked to **Project A**.
4. Create a vendor bill (or expense) whose AML is distributed between **AA1**
and **AA2**, where **AA2** is processed after **AA1**.
5. Validate the document.
**Expected behavior:**
The move line should still be mapped to **SO1** because **AA1** references
**Project A**.
**Actual behavior:**
The last processed analytic account (**AA2**) overwrites the previously found
project, causing the move line not to be linked to **SO1**.
### After this fix
---
* `project_per_accounts` stores **all** projects associated with each analytic
account instead of keeping only the last one.
* The project lookup preserves all valid project candidates instead of
overwriting previously found results during iteration.
* As a result, the method can resolve the related `sale.order` in more cases,
improving the overall accuracy of the mapping.
> **Note:** This change prevents valid project associations from being lost
> when multiple projects share an analytic account or when multiple analytic
> accounts are processed for the same move line.
**OPW:** 6294615
Forward-Port-Of: odoo/odoo#277110Shipping costs are now recalculated when a returning shopper confirms a cart after product prices have changed. This prevents orders from incorrectly keeping free shipping when the updated cart total no longer qualifies, while preserving pickup location selections for click-and-collect orders.
Original PR description
Steps to reproduce ================== 1. Configure a delivery method with free shipping above a threshold 2. Add a product to the cart above that threshold, select the delivery method and leave the…
Steps to reproduce ================== 1. Configure a delivery method with free shipping above a threshold 2. Add a product to the cart above that threshold, select the delivery method and leave the cart unfinished 3. Lower the product price below the threshold 4. Recover the cart and confirm the order from /shop/checkout => The product prices are refreshed, but shipping stays free although the new total is below the threshold. Root cause ========== Since [1], nothing re-rates the carrier after /shop/confirm_order refreshes the cart prices: the delivery method is selected before the confirmation. In 17.0, the payment page auto-clicked the selected carrier on load, which re-rated the shipping cost and masked the issue. Fix === Re-rate the selected delivery method in `shop_confirm_order` after the prices have been recomputed, as `_cart_update` already does. [1]: https://github.com/odoo/odoo/commit/8e2b6cede55b51f7ccdbe7601aa7e6035fd6f9fe opw-6383849 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283684 Forward-Port-Of: odoo/odoo#276905
Invalid VAT warning messages now preserve and display the full VAT number entered by the user, such as a Swiss VAT number with its country prefix. This reduces confusion by showing the exact value that failed validation, making it easier for users to correct contact tax details.
Original PR description
Before this change: When entering or importing a VAT number (e.g., CHE-115.391.649), an invalid VAT warning displays a string missing its country_id (e.g., E-115.391.649). This confuses users and masks the actual input string that triggered the validation failure. To reproduce: 1. Open any contact record and set the Country to Switzerland. 2. Enter an invalid or manually formatted Swiss VAT number like `CHE-115.391.649`. 3. Save or trigger the VAT validation check. 4. Observe the warning banner showing `E-115.391.649` instead of `CHE-115.391.649`. After this change: The validation warning logic preserves the original user input when constructing the alert message, ensuring error notifications accurately display VAT number. Issue introduced by: * https://github.com/odoo/odoo/commit/ac95d2d6d80a368dfb190d0ac21da2af479a8488 * https://github.com/odoo/odoo/commit/a2afe3292e1cd0a4f339dc47707e469653d13ea0 opw-6474217 Forward-Port-Of: odoo/odoo#284305
The French PDP registration wizard no longer shows an unnecessary “Production” label when users are already in production mode. This removes confusing wording and makes the registration screen clearer for business users.
Original PR description
It makes no sense to mention (Production) on pdp registration wizard when you are in prod mode Forward-Port-Of: odoo/odoo#280501 Forward-Port-Of: odoo/odoo#280360
This fix prevents errors when retrying rejected Saudi simplified consumer invoices with ZATCA. The system now regenerates the required QR code from the invoice submission data during retry, helping businesses resubmit invoices without manual intervention while keeping existing QR display rules unchanged.
Original PR description
- When a simplified (B2C) invoice is rejected by ZATCA and subsequently retried, its state remains rejected. The QR code computation therefore returns an empty value for the rejected invoice, as the computation is primarily intended for the post-EDI state. During the retry, this results in a traceback when the QR code is applied to the XML. - Regenerate the QR code directly from the submission data when preparing a new B2C XML, instead of relying on the state-dependent QR code field. This preserves the existing QR visibility rules for the final invoice. task-6485555 Forward-Port-Of: odoo/odoo#283534
This fixes an internal test issue in the Appraisals module that could fail when run around midnight. It helps keep automated checks stable without changing how employees or managers use the appraisal features.
Original PR description
### Explanation When `test_hr_appraisal` is run at, for example, 23:59:59, the line `self.hr_employee2.next_appraisal_date = date.today()` is executed after midnight, on the following day. As a result, a validation error is raised: `odoo.exceptions.ValidationError: You cannot set 'Next Appraisal Date' in the past.`
Invoices created from Chilean Point of Sale orders are now automatically submitted to SII as expected. This prevents businesses from having valid PoS invoices left unsent after closing the register, reducing manual follow-up and compliance risk.
Original PR description
Issue: Invoices from PoS orders are not automatically send to SII. Steps to reproduce: - Open PoS - create an order - add a company as customer - pay - close register - go to invoice Current behavior: Invoice is created but not sent Expected behavior: Invoice is created and send to SII. Cause: Before 19.2 invoices were sent using a cron. Starting from 19.2, invoices are sent using the Send button of the invoice form. In order to get a perf improvement, PDF generation was deactivated for l10n_cl PoS invoices at creation. However, the same method used to generate the PDF is used to send the invoice to SII. Therefore, invoices from PoS were not sent to SII. opw-6423528 Forward-Port-Of: odoo/enterprise#126623
This fix prevents duplicate emails from being sent when employees submit expenses across multiple companies. It helps keep expense approval communications clearer and avoids unnecessary repeated notifications.
Original PR description
Fix a small issue resulting in mail duplication when submitting expenses from multiple companies that appeared in the infamous 704a5a19 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#284861 Forward-Port-Of: odoo/odoo#283013
Appointment invitation emails now generate public calendar links correctly by using the proper permission level for calendar access tokens. This prevents invitation emails from failing to render, helping customers and attendees receive appointment details without interruption.
Original PR description
Since calendar attendee access tokens are restricted to system users, appointment mail templates must sudo token reads when generating public calendar links. This follows the same pattern as the calendar mail templates and avoids an AccessError when rendering attendee invitation emails. ref: https://github.com/odoo/enterprise/commit/88a3cca752a5f726cd0260b485fc93f65a268cf8 Task-4711415 Forward-Port-Of: odoo/enterprise#129511
Unreconciling one bank statement line from an invoice or bill now only removes that specific reconciliation instead of clearing all related reconciliations. This prevents invoices and bills from being accidentally fully unreconciled when only one bank transaction needs correction.
Original PR description
**STEP TO REPRODUCE** 1. Create a bill or an invoice. 2. Create multiples bank statement. 3. Reconciles those bank statements to the invoice/bill. 4. Unreconciles one of those bank statement on the invoice/bill. 5. Notice the invoice/bill is completely unreconciled. Expected behavior: only the unreconciled line should be unreconciled. **CAUSE** When unreconciling a partial linked to a bank statement, we call `delete_reconciled_line()` on both `partial.credit_move_id` and `partial.debit_move_id`. One on those is the the payment_term line of the invoice/bill the bank statement line is reconciled with. This payment_term line is also linked to all partial reconcilliation line on the invoice/bill, so calling `delete_renconciled_line()` delete all the reconciled line of the invoice/bill. **FIX** We should call `delete_renconciled_line()` only on the bank statement move line, not on the payment term line. opw-6465096 Forward-Port-Of: odoo/enterprise#128126
This fix ensures Sendcloud shipping label requests keep the selected label format, such as ZPL or PDF, instead of being unintentionally reset. Businesses using non-PDF label printers should receive the correct label type again, reducing manual work and printing issues.
Original PR description
We send the label type we want to get (zpl, pdf, ...) in the request headers. However, since odoo/enterprise#115999, we also send the partner ID in the headers. This was overriding the headers passed to the method, making Sendcloud always return a PDF label. Forward-Port-Of: odoo/enterprise#129542
Attendance officer access is now aligned so these employees are also treated as regular internal users. This keeps attendance permissions consistent across supported versions and avoids access issues without changing expected business workflows.
Original PR description
In [this forward port in 19.4](https://github.com/odoo/odoo/pull/281713/changes#diff-4b6f4473332f5e30b7d83acb51946ffa4731af0093c75954f619906010c3f8a8R28), I have changed the `implied_ids` of `group_hr_attendance_officer` as well. This PR reflects the change on other stable versions. The change should not break permissions, as an attendance officer should be a user, and `base.group_user` implies the group `hr_attendance.group_hr_attendance_own_reader` task-6499161 Forward-Port-Of: odoo/odoo#284414 Forward-Port-Of: odoo/odoo#284191
Fixed an issue where AI chat image generation could fail because the system looked for missing provider information. The provider is now determined from the selected model, allowing image requests in chat to complete reliably.
Original PR description
In the changes introduced in [this forward port](https://github.com/odoo/enterprise/pull/128330), `_ai_tool_generate_image` tool call reads `tool_context['provider']`, but that key is never set when `tools_context` is built. Any request to the agent chat that triggered image generation raised `KeyError('provider')`.
Derive the provider from the model name instead, the same way the rest of the codebase does in saas-19.3.
related PR: odoo/enterprise#128330
Forward-Port-Of: odoo/enterprise#129562This fixes an automated barcode manufacturing flow where a scrap quantity could be lost while the form was still updating after product selection. The change helps ensure scrap operations are recorded with the intended positive quantity instead of failing with an error.
Original PR description
Selecting the product in the scrap form triggers a `stock.move` onchange. The quantity step only waited for the input to exist, not for that onchange to be applied, so the value could be written while it was still in flight and be reset to 0 by its response. It was also assigned directly on the input, without any event, so the field was never flagged as dirty. The scrap was then recorded with a quantity of 0 and `action_scrap` rejected it with "You can only enter positive quantities.". Wait for the quantity input to hold its post-onchange value before typing, and dispatch an input event, like the other scrap tours already do. error-238911 Forward-Port-Of: odoo/enterprise#129410 Forward-Port-Of: odoo/enterprise#128931
POS managers can now open and manage payment method settings without running into an access error. The change gives them the needed read-only access to online payment provider information while keeping the field hidden from users who should not use it.
Original PR description
Only admin users have read access to the `payment.provider` model. Opening the PoS payment method form as a non-admin would raise an access error because the `online_payment_provider_ids` many2many field tries to fetch `payment.provider` records on form load. Grant read-only access on `payment.provider` to `group_pos_manager` so POS admins can use the field. Restrict the field's group in the form view to `point_of_sale.group_pos_manager,base.group_system` so it is not rendered for users without either role. opw-6208656 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277256 Forward-Port-Of: odoo/odoo#263837