Tuesday, September 22, 2026
37 changes · 19.0
New functionality added to Odoo
Swiss payroll now supports daily allowance entries that keep an employee's net pay the same when employers promise compensation during sickness, accident, maternity, military service, disability, or loss-of-earnings absences. The system automatically calculates the needed net/gross adjustment while preserving manual override options and matching accounting treatment.
Original PR description
Daily allowances paid by an insurance (sickness, accident, maternity, military service, disability, loss of earnings) are entered as third party payments: the automatic 2050 line deducts them from…
Daily allowances paid by an insurance (sickness, accident, maternity, military service, disability, loss of earnings) are entered as third party payments: the automatic 2050 line deducts them from the payslip so the employee is not paid twice, and takes them out of the insurance bases. Because that insurance money is free of social contributions, the deductions no longer match a normal month and the final net drifts away from what the employee normally receives. Some employers promise the employee the exact same net as if nothing had happened. This commit adds a "(Net compensation)" variant of each daily allowance wage type to express that promise: when one of them is used, the payslip computes wage type 4900 (Net/Gross compensation) on its own, with the amount that brings the final net back to exactly what it would be without the allowance lines. The employer takes over, or gets back, the whole difference in contributions and source tax. There is no direct formula for that amount, since the 4900 line changes its own deductions (source tax brackets, insurance ceilings, 5 cents rounding). The payslip is therefore recomputed a few times with different amounts until the net matches, each attempt running inside a database savepoint that is rolled back afterwards, so nothing of those attempts is kept. A manual 4900 input keeps priority over the automatic computation, the same way a manual 2050 input does. The new wage types get the same accounting mapping as their normal counterparts. Forward-Port-Of: odoo/enterprise#128243
Enhancements to existing features
Automatic bank reconciliation now matches payment references to invoice names even when uppercase and lowercase letters differ. This reduces manual reconciliation work caused by simple formatting differences in customer payment references.
Original PR description
Currently, when a payment reference isn't in the same letter case as the invoice name, it wouldn't automatically reconcile. task-6562440
Resolved issues and error corrections
The HTML editor now safely handles selections that start inside the editor and extend outside it, such as using Ctrl+A or dragging beyond the editor area. This prevents a browser-specific error in Chrome and Edge, making editing more reliable for users.
Original PR description
## Description Fixes issue #187539 where selecting text that extends outside the HTML editor boundaries throws an uncaught promise error. ## Problem When using Ctrl+A or dragging a selection from inside the editor to outside its boundaries, the `updateSelectionTable` method in `table_plugin.js` would process the invalid selection and call `setSelection()` with nodes outside the editable area, causing: This only affected Chromium-based browsers (Chrome, Edge) as Firefox already had protection against this scenario. ## Solution Added a safety check in `updateSelectionTable` to verify that the document selection is within the editable area before processing table selection updates. This extends the same protection Firefox had to all browsers. ## Changes - `addons/html_editor/static/src/main/table/table_plugin.js`: Added validation check - `addons/html_editor/static/tests/table/selection.test.js`: Added regression test Closes #187539
Duplicating a section in a sales quotation now keeps any manually adjusted unit prices on the copied product lines. This prevents incorrect price resets and helps sales teams maintain accurate quotes without re-entering custom pricing.
Original PR description
Problem: When duplicating a section containing sale order lines, the system should preserve any manually modified unit prices on those lines. However, the system would reset the unit price back to…
Problem: When duplicating a section containing sale order lines, the system should preserve any manually modified unit prices on those lines. However, the system would reset the unit price back to the product's default because the duplication process triggers a standard product onchange, which wipes the custom price on the new duplicated order line. Solution: Override onchange_batch on sale.order.line to intercept line initialization during a duplication. If a copied price_unit is present in the payload, the method restores that modified price and recalculates the subtotal and tax amounts accordingly, ensuring the duplicated line accurately reflects the original line's pricing. Steps to reproduce(runbot v19): 1. Create a Quotation and add a product line under a section. 2. Manually modify the unit price on the product line to a custom value (e.g., set $10 to $99). 3. Click the section dropdown menu and click Duplicate. 4. Notice the duplicated product line incorrectly resets its unit price back to the default price ($10) instead of preserving the manually modified price ($99). opw-6421922
This fix prevents an error when users select text from inside the HTML editor and extend the selection outside the editor boundaries. It makes the editor more stable in Chrome and Edge and aligns behavior with Firefox, reducing interruptions while editing content.
Original PR description
## Description Fixes issue #187539 where selecting text that extends outside the HTML editor boundaries throws an uncaught promise error. ## Problem When using Ctrl+A or dragging a selection from…
## Description Fixes issue #187539 where selecting text that extends outside the HTML editor boundaries throws an uncaught promise error. ## Problem When using Ctrl+A or dragging a selection from inside the editor to outside its boundaries, the `updateSelectionTable` method in `table_plugin.js` would process the invalid selection and call `setSelection()` with nodes outside the editable area, causing: ``` UncaughtPromiseError: Selection is not in editor ``` This only affected Chromium-based browsers (Chrome, Edge) as Firefox already had protection against this scenario. ## Solution Added a safety check in `updateSelectionTable` to verify that the document selection is within the editable area before processing table selection updates. This extends the same protection Firefox had to all browsers. ## Changes - `addons/html_editor/static/src/main/table/table_plugin.js`: Added validation check - `addons/html_editor/static/tests/table/selection.test.js`: Added regression test ## Testing The new test verifies that selecting from inside a table to outside the editor does not throw an error. Closes #187539
Payslips validated from the list view now correctly generate or queue their PDF attachments. This prevents missing payslip documents and removes the need for users to click a separate confirmation action.
Original PR description
Validating payslips through the Payslips list view's "Validate" header button does not generate their PDF, nor does it queue them for the "Payroll: Generate pdfs" scheduled action to pick up later.…
Validating payslips through the Payslips list view's "Validate" header button does not generate their PDF, nor does it queue them for the "Payroll: Generate pdfs" scheduled action to pick up later. The PDF only gets generated if the user separately clicks the "Confirm" button afterwards.
### Steps to reproduce:
- Install Payroll with demo data.
- Go to Payroll > Payslips > Pay Run, generate payslips for a few
employees ("Regular Pay").
- Select one or more of those draft payslips in the list view, and click the
"Validate" header button.
OR
- Run the "Payroll: Generate pdfs" scheduled action.
- Open any affected payslip.
### Observed behavior:
No PDF attachment is ever created for the payslips validated this way, even after the cron runs.
### Expected behavior:
PDF attachment should be generated when validating the payslip, via "Validate" Button or cron
### Root cause:
[action_payslip_done()](https://github.com/odoo/enterprise/blob/7b309f2e74f9fa37b109375de6387db3241c2bcb/hr_payroll/models/hr_payslip.py#L607-L641) only runs its PDF-generation block when `self.env.context.get('payslip_generate_pdf')` is truthy. The Payslips list view's "Validate" button at [1] was missing
`context="{'payslip_generate_pdf': True}"`, unlike the "Confirm" button right next to it and the Pay Run's own "Validate" button, both of which already carry it. Without it, the whole PDF block is silently skipped: queued_for_pdf never gets set, so there is nothing for the cron to act on either.
[1]-
https://github.com/odoo/enterprise/blob/7b309f2e74f9fa37b109375de6387db3241c2bcb/hr_payroll/views/hr_payslip_views.xml#L10-L12
### FIX:
Add the missing context="{'payslip_generate_pdf': True}" to the "Validate" button in the Payslips list view header.
**opw-6401393**This fixes the loyalty program setup screen so minimum purchase options are fully hidden for buy X get Y promotions. It prevents users from setting an irrelevant condition on those reward programs, reducing configuration mistakes.
Original PR description
I think the point was to hide the entire `Minimum Purchase` section when the program type is `buy_x_get_y`, but only the label is hidden. This commit will also hide the div under the label, so no minimum purchase can be set on `buy_x_get_y` programs --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The HTML editor now safely handles selections that start inside the editor and extend outside it. This prevents a browser-specific error in Chrome and Edge, making editing more reliable for users working with tables or selecting large blocks of content.
Original PR description
## Description Fixes issue #187539 where selecting text that extends outside the HTML editor boundaries throws an uncaught promise error. ## Problem When using Ctrl+A or dragging a selection from…
## Description Fixes issue #187539 where selecting text that extends outside the HTML editor boundaries throws an uncaught promise error. ## Problem When using Ctrl+A or dragging a selection from inside the editor to outside its boundaries, the `updateSelectionTable` method in `table_plugin.js` would process the invalid selection and call `setSelection()` with nodes outside the editable area, causing: ``` UncaughtPromiseError: Selection is not in editor ``` This only affected Chromium-based browsers (Chrome, Edge) as Firefox already had protection against this scenario. ## Solution Added a safety check in `updateSelectionTable` to verify that the document selection is within the editable area before processing table selection updates. This extends the same protection Firefox had to all browsers. ## Changes - `addons/html_editor/static/src/main/table/table_plugin.js`: Added validation check - `addons/html_editor/static/tests/table/selection.test.js`: Added regression test ## Testing The new test verifies that selecting from inside a table to outside the editor does not throw an error. Closes #187539
Fixes two Slovak accounting setup entries that assigned fixed assets to the wrong balance sheet accounts. This prevents vendor bills from creating assets under land or the wrong asset category, improving accuracy of depreciation and financial reporting for Slovak companies.
Original PR description
In `account.asset-sk.csv` the asset-account column has slipped by one row for the last two entries, so each of those two models pairs an asset account with the accumulated depreciation account of a…
In `account.asset-sk.csv` the asset-account column has slipped by one row for the last two entries, so each of those two models pairs an asset account with the accumulated depreciation account of a different asset class.
As shipped (Slovak account names glossed in English):
sk_asset_basic_herd_and_draft_animals
asset 029000 Ostatný dlhodobý hmotný majetok "Other tangible fixed assets"
depreciation 086000 Oprávky k základnému stádu "Accumulated depreciation, basic herd"
sk_asset_other_tangible_fixes_assets
asset 031000 Pozemky "LAND"
depreciation 089000 Oprávky k ostatnému DHM "Accumulated depreciation, other tangible"
Every other row in the file pairs an account with the accumulated depreciation account that names it: 012000 with 072000, 021000 Stavby ("Buildings") with 081000 Oprávky k stavbám ("Accumulated depreciation, buildings"), and so on. Only these two break the pattern, and both break it the same way.
`account.account-sk.csv`, in the same directory, disagrees with them and is right: it links 026000 Základné stádo a ťažné zvieratá ("Basic herd and draft animals") to the herd model and 029000 to the other-tangible one, and gives 031000 Pozemky no asset model at all, land not being depreciable under Slovak law or any other.
The consequence is not cosmetic.
`account.account.asset_model_ids` creates an asset when a vendor bill line hits the account, and the asset takes its accounts from the model. So on a Slovak database a bill booked to 029000 "Other tangible fixed assets" produces an asset whose value sits on 031000 "Land" and depreciates over six years, while the account that should have carried it never does.
Corrected to the accounts that both the accumulated depreciation side and `account.account-sk.csv` point at:
sk_asset_basic_herd_and_draft_animals asset 026000 Základné stádo a ťažné zvieratá "Basic herd and draft animals"
sk_asset_other_tangible_fixes_assets asset 029000 Ostatný dlhodobý hmotný majetok "Other tangible fixed assets"
Verified by loading the SK chart on a clean database: all ten asset models now pair with their own accumulated depreciation account, and none points at 031000.
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-prPoint of Sale now blocks opening a register when the server connection is unavailable instead of saving the action for later. This prevents sessions from closing with sales while never being properly opened on the server, ensuring opening dates, session names, and cash amounts are recorded correctly.
Original PR description
Steps to reproduce: - Open a PoS so that the Opening Control popup is displayed. - Cut the connection to the server without the browser going "offline" (e.g. the router stays up but the internet link…
Steps to reproduce: - Open a PoS so that the Opening Control popup is displayed. - Cut the connection to the server without the browser going "offline" (e.g. the router stays up but the internet link is down). - Click on "Open Register". - Restore the connection, make some orders, close the session. Issue: The session is closed with orders but was never opened on the server: it has no opening date, keeps its temporary name "<config>/00000", and the opening cash was never recorded. `set_opening_control` was called with the `queue` flag. On a ConnectionLostError the call was silently pushed to the in-memory unsync queue, and the popup marked the session as opened locally and closed itself. That queue is only replayed on the browser `online` event, which is never fired when the device stayed connected to its local network, and it is lost when the tab is closed or reloaded. Meanwhile the server accepts orders on a session in `opening_control`, and nothing checks it at closing. Opening the register is not an operation that can be deferred: the session must be opened before any order is made. The call is no longer queued. When the connection is lost, the user is told that the register cannot be opened offline, and the popup stays open so the opening can be retried once the connection is back. opw-6580262 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288899
Dates and date-times now appear in the right format when administrators set default values in debug mode. This reduces confusion and helps prevent incorrect date values from being saved.
Original PR description
Problem: When setting default values in debug mode, the date and datetime values are not formatted correctly. This leads to confusion and incorrect values being set for date fields. Steps to reproduce: 1. Enable debug mode 2. Create a journal entry, set the date to some date (August 31st, for example), and save. 3. Click the bug icon, click Set default values. 4. Click on the dropdown, notice how the date is displayed as datetime instead of date. Cause: The displayed values were not being formatted for date and datetime fields when setting default values. They were just output as strings. Also, dates were not serialized correctly before being sent to the server, they were serialized as datetime instead of date, because the code was checking the constructor name of the value instead of the field type. opw-6533491 Forward-Port-Of: odoo/odoo#287813
This fixes a visual mismatch in quotations when optional products are shown in dark mode. The optional products table now blends with its surrounding panel, providing a more consistent and polished sales interface.
Original PR description
**Steps to reproduce:** - Set preferences to dark mode - Create a quotation and add a product with optional products (e.g customizable desk) -> Background of optional products is lighter than the…
**Steps to reproduce:** - Set preferences to dark mode - Create a quotation and add a product with optional products (e.g customizable desk) -> Background of optional products is lighter than the surrounding div **Behavior:** The `div` showcasing optional products is set to be a bit darker to add contrast in the modal. In light mode, table's background is transparent, which allows it to adapt to a darker container. However, this is not the case in dark mode, which causes a mismatch between table and div backgrounds. By default `$table-bg` follows `$body-bg`: https://github.com/odoo/odoo/blob/3442814d57cd9420d7dc6d022d15cd5d1b3def74/addons/web/static/lib/bootstrap/scss/_variables.scss#L738-L740 And is later set to transparent here: https://github.com/odoo/odoo/blob/3442814d57cd9420d7dc6d022d15cd5d1b3def74/addons/web/static/src/scss/bootstrap_overridden_frontend.scss#L74-L75 However when darkmode is enabled, `$body-bg` is re-assigned, which forces `$table-bg` back to the default dark color, bypassing the transparent override. https://github.com/odoo/odoo/blob/3442814d57cd9420d7dc6d022d15cd5d1b3def74/addons/web/static/lib/bootstrap/scss/_root.scss#L132-L139 --- This commit ensures that `table-bg` is explicitly set to transparent for tables displaying optional products. opw-6569713
Product images are now preserved when multiple variants of the same product are created or imported at the same time. This prevents images from disappearing during CSV imports, helping product catalogs remain accurate without manual rework.
Original PR description
Steps to reproduce: - Import a product CSV with one row per attribute value ("Product Values" column) and an image URL on every row, or create several variants of the same template in one `create()`…
Steps to reproduce:
- Import a product CSV with one row per attribute value ("Product Values" column) and an image URL on every row, or create several variants of the same template in one `create()` call with an image.
- Open the product: neither the template nor the variants have an image. Products without attribute values imported in the same file are fine.
The import creates all variants of a template in a single batch. The inverse of `image_1920` is applied record by record: the first variant finds no image on the template and writes its image there. Writing `image_1920` on the template invalidates the image cache of every product.product, including the sibling variants being created, whose `image_1920` is protected during the inverse and therefore reads back as empty. The next variant then takes the "clear the image from the template" branch and writes `False` on the template, undoing the first write. Nothing is stored.
Read the value to inverse for every record before writing anything, so the invalidation triggered by the template write cannot change what the following records write.
opw-6545651
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis fixes an issue where link previews in the HTML editor could show an empty clickable area when website metadata contained only spaces. The editor now ignores blank metadata and falls back to the link URL or an empty value, making previews clearer for users.
Original PR description
Problem: When a link's metadata contains whitespace-only strings for `og_title`, `og_description`, or `og_image` (e.g. `og_title: " "`), the link popover displays an empty clickable preview area.…
Problem: When a link's metadata contains whitespace-only strings for `og_title`, `og_description`, or `og_image` (e.g. `og_title: " "`), the link popover displays an empty clickable preview area. Cause: `LinkPopover` assigned raw metadata values directly. Non-empty whitespace strings evaluate to truthy values in JavaScript (`" "` is truthy), preventing fallback to the default URL or empty string. Solution: Trim the metadata values (`og_title`, `og_description`, `og_image`) when populating state so that whitespace-only values evaluate to empty strings and trigger appropriate fallbacks. Steps to reproduce: - Open HTML editor. - Add a link with URL `https://netorg4182089.sharepoint.com/:v:/s/projects/IQD72ajP3WOBT4jcAu_1qLfIAamL9lvrq4ls1Bs9XCyXJvw?e=vQ9wH3`. - Open the link popover. => Observe that the popover title falls back to the URL instead of showing an empty clickable space. opw-6564118 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Live Chat now checks URL matching rules before they are saved, preventing invalid entries from breaking chat initialization on the website. This helps administrators catch configuration mistakes early and keeps the website experience stable for visitors.
Original PR description
Currently, an error occurs when a user opens the website and Live Chat is initialized with an invalid URL regex. Steps to Reproduce: - Install `website_livechat` without demo data. - Open `Live Chat`…
Currently, an error occurs when a user opens the website and Live Chat is initialized with an invalid URL regex. Steps to Reproduce: - Install `website_livechat` without demo data. - Open `Live Chat` and, in the `YourWebsite.com` channel, click the `three-dot` menu and select `Configure Channel`. - Go to the `Rules` tab. - Add a rule, select `Welcome Bot` in `Chatbot`, and set an invalid regex such as `*` in `URL Regex` and save. - Click `Join Channel`. - Open the `website`. `re.PatternError: nothing to repeat at position 0 when serializing dict item 'result' ` - when `URL Regex is '['` `re.error: unterminated character set at position 0 when serializing dict item 'result'` When a user opens the website, Live Chat is initialized by checking operator availability and finding the matching country/URL rule [1]. The rule matching checks whether `regex_url` matches the current page URL. It uses `re.search()` [2], which compiles the regex pattern before searching. If `regex_url` contains an invalid pattern such as `*`, `re.search()` raises `nothing to repeat at position 0` because `*` must follow a preceding regex element. Similarly, `[` starts a character set (`[...]`) but has no closing `]`, causing `unterminated character set at position 0`. This commit adds a constraint to validate `regex_url` and reject invalid regex patterns when creating or updating Live Chat rules. [1]- https://github.com/odoo/odoo/blob/f962f1eccecfdbc95375f795174c8f687459b521/addons/im_livechat/controllers/main.py#L87 [2]- https://github.com/odoo/odoo/blob/f962f1eccecfdbc95375f795174c8f687459b521/addons/im_livechat/models/im_livechat_channel.py#L387 sentry-7679767396 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents an employee-related error from interrupting upgrades in the Australian payroll module. It ensures payroll warning calculations use the correct employee reference, improving upgrade reliability without changing business workflows.
Original PR description
When we replace read_group usage from
the business code with _read_group,
`proportions` return dictionary with
employee recordset instead of id.
and later we try to fetch employee id
from proportions keys which is not available
so we got keyerror during upgrade.
```
File "/home/odoo/src/odoo/19.0/addons/mail/models/mail_thread.py", line 483, in _compute_field_value
return super()._compute_field_value(field)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4949, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 81, in determine
return needle(*args)
File "/home/odoo/src/enterprise/19.0/l10n_au_hr_payroll/models/hr_employee.py", line 118, in _compute_proportion_warnings
proportions[emp.id] * 100,
KeyError: 4
```This fix ensures rental planning tests correctly account for time zones when comparing scheduled rental times. It prevents false test failures during early morning hours, helping keep release validation stable without changing customer-facing behavior.
Original PR description
**Issue:** `test_payment_renting_product_available` test is failing when executed between 0:00 AM and 2:00 AM in Brussels timezone (UTC+2): ``` AssertionError: datetime.datetime(2026, 6, 22, 16, 0) != FakeDatetime(2026, 6, 21, 16, 0) : The planning slot should begin at the same time as the picking time. ``` In the database the datetime is stored in UTC, which is the previous day for the example above. In `test_payment_renting_product_available` test, the datetime is passed to `datetime.combine()` function that naively uses the date part, which leads to a one-day delta. The datetime should be converted to the working timezone before being passed to `datetime.combine()`. runbot-940435
Colombian electronic invoices sent to DIAN no longer include the journal's technical control key in the invoice note field. This keeps the XML note limited to the invoice Terms and Conditions, avoiding incorrect information being submitted to the tax authority.
Original PR description
**Steps to reproduce:** - Install the `l10n_co_dian` module and switch to the CO Company. - Disable `Test environment` and enable `DIAN Demo Mode` in the invoicing settings. - Set a `Technical…
**Steps to reproduce:** - Install the `l10n_co_dian` module and switch to the CO Company. - Disable `Test environment` and enable `DIAN Demo Mode` in the invoicing settings. - Set a `Technical control key` on the `Customer Invoices` journal. - Create and confirm an invoice with Terms and Conditions. - Send the invoice to `DIAN`. - Open the generated XML file and observe the `cbc:Note` tag. **Observation:** The `Note` tag contains the `technical control key`. **Expected behavior:** The `Note` tag should only contain the Terms and Conditions value from the invoice. (Confirm with PO [1]) **Root Cause:** At [2] and [3], the code includes the `technical key` in the `Note` tag. [1]: https://www.odoo.com/mail/message/1164671931 [2]: https://github.com/odoo/enterprise/blob/a5f4bde1aa33ea7b796d7ee3f9d713a6c8ab8348/l10n_co_dian/models/account_edi_xml_ubl_dian.py#L664 [3]: https://github.com/odoo/enterprise/blob/a5f4bde1aa33ea7b796d7ee3f9d713a6c8ab8348/l10n_co_dian/models/account_edi_xml_ubl_dian.py#L1600-L1603 opw-6513843 Forward-Port-Of: odoo/enterprise#131495
The Barcode app now assigns scanned package quantities to the correct lot for products tracked by lots. This prevents over-counting and ensures warehouse receipts reflect the intended quantities when staff scan packaging and lot numbers.
Original PR description
Issue ===== Currently, the Barcode app doesn't work very well when the user scans packaging and lots. If they want to apply one or multiple packagings to a specific lot, the quantity is not quite…
Issue ===== Currently, the Barcode app doesn't work very well when the user scans packaging and lots. If they want to apply one or multiple packagings to a specific lot, the quantity is not quite exact since scanning a lot already increases the quantity by 1. Also, if there are multiple lots, the packaging quantity isn't added to the last scanned lot which is problematic. How to reproduce ================ 1. Active "Units of Measure & Packagings" and "Lots & Serial Numbers" settings; 2. Create a product tracked by lots with a barcode; 3. In the "Sales" tab, add a packagings (for example, "Pack of 6"); 4. Configuration > Units & Packagings > Selected the added UoM > Click on "Packaging Barcodes"; 5. Create a new barcode for the created product; 6. Create and confirm a receipt for 12 units of this product and open the operation in Barcode app; 7. Scan the product's barcode, then a lot then the packaging barcode -> First issue: the line has 7 units. 8. Scan a second lot and scan the packaging again -> Second issue: the packaging quantity is added to the first line, not the second one. Explanation =========== About the first issue, it is the expected behavior. When the user scans a lot, we increase the line quantity by 1. When the user scans a packaging, we increase the line quantity by this packaging quantity. The issue here is the user expects to scan a lot and then to apply a packaging quantity to it. For example, scanning a lot then a pack of six should result by a line with 6 units, not 7. Fix === With this commit, the scanned quantity (thus the scanned packaging) for product tracked by lots aren't apply directly on some line but keep in a buffer, with a message to let the user what they scanned until here. Then, when the user scans a lot, only then the quantity is assigned to the scanned lot. So, the logic and expected flow is: 1. The user scans a product or a packaging; 2. Then they scan a lot; 3. The previously scanned quantity is assigned to the scanned lot. [task-6216921](https://www.odoo.com/odoo/966/tasks/6216921)
Helpdesk reports now calculate SLA success consistently with the underlying tickets. This prevents report totals from disagreeing with the ticket lists opened from those totals, giving managers more reliable SLA performance data.
Original PR description
1. Open Helpdesk > Configuration > SLA Policies and create a policy on the team "Customer Care" with "In Progress" as target stage 2. Create two tickets on that team and move them to "In Progress"…
1. Open Helpdesk > Configuration > SLA Policies and create a policy on the team "Customer Care" with "In Progress" as target stage 2. Create two tickets on that team and move them to "In Progress" well before their deadline, so their SLA is reached on time, and leave a third one in "New" with its SLA still running 3. Open Helpdesk > Reporting > Ticket Analysis, enable the "SLA Success" filter and group by "Helpdesk Team", then by "Stage" 4. Click the cell of the "In Progress" stage -> the list of tickets it opens does not hold the number of tickets the cell was showing Ticket Analysis only offers aggregated views, so its cells count rows of helpdesk.ticket.report.analysis while clicking one re-queries helpdesk.ticket, and the two evaluate sla_success differently. Both analysis reports compute it as "the deadline lies in the future", which is what helpdesk.ticket.sla_success did until odoo/enterprise#85412 aligned it on "reached before the deadline". The two SQL views were left behind, so they label an ongoing SLA as a success and a reached one as a failure once its deadline has passed. With this commit, Ticket Analysis mirrors helpdesk.ticket.sla_success and SLA Status Analysis mirrors the 'reached' branch of its own sla_status, so the cells agree with the tickets they drill down to. Task-6523661
The default Terms and Conditions page now keeps interactive content blocks such as accordions intact when edited and saved. This prevents broken layouts and errors when users add or update accordion items in invoice terms content.
Original PR description
# How to reproduce - Go to Settings > Default Terms & Conditions > Preview - Open the editor - Add a Content Block - Add an Accordion in that Block - Save # The issue The accordion gets messed up and…
# How to reproduce - Go to Settings > Default Terms & Conditions > Preview - Open the editor - Add a Content Block - Add an Accordion in that Block - Save # The issue The accordion gets messed up and trying to add a new item to that accordion raises a traceback # Cause `invoice_terms_html` is an html field with some sanitization enabled : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/account/models/company.py#L172 This sanitization will remove the accordion snippet's buttons that controls the functionality of the snippet : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/website/views/snippets/s_accordion.xml#L9 This issue was already addressed by : https://github.com/odoo/odoo/commit/b6b4db5fb5690436a4284f6a22abf9f3b346a324 But it is not enough in the case of the accordion. The buttons will be removed by the lxml clean.Cleaner : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/odoo/tools/mail.py#L364 # Proposed Solution Disable sanitization entirely, like for blog's content, which can also be edited in the website editor : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/website_blog/models/website_blog.py#L29 opw-6500378 Forward-Port-Of: odoo/odoo#285255
This fix ensures the point-of-sale loyalty refund test loads the required customer record before trying to select it. It prevents test failures when the customer is not included in the initial contact list, improving reliability without changing user-facing behavior.
Original PR description
In the case that there are too many Contact records for 'Refunding Guy' to be in the first 100 records processed when opening the Partner List, the test errors as a result of trying to click on a Customer that is not present. A step has been added to ensure that the 'Refunding Guy' record is loaded before attempting to click on it. runbot-939288
This fix ensures Spanish TicketBAI/Batuz credit notes keep the equivalence surcharge rate as a positive percentage. This prevents tax authority validation errors and allows affected credit notes to be sent successfully.
Original PR description
In credit notes, `TipoRecargoEquivalencia` was multiplied by the reversal sign, producing negative values (e.g. -1.40) that violate the Batuz `Tipo3.2Type` pattern and get rejected by the tax agency (`B4_2000001: cvc-pattern-valid`). Unlike `BaseImponible`/`CuotaImpuesto` (amounts that must be negative), `TipoRecargoEquivalencia` is a percentage and must stay positive, as `TipoImpositivo` does. Steps to reproduce: 1. Configure a Spanish company with TicketBAI (Bizkaia). 2. Create a credit note (out_refund) with an equivalence surcharge tax. 3. Send it to TicketBAI; the send fails with a schema validation error. Task: MT-15939 OPW: https://www.odoo.com/es_ES/my/tasks/6573801 @jco-odoo could you review? It's essential to be able to send to Tbai/Batuz with equivalence surcharge tax. Forward-Port-Of: odoo/odoo#289618
Click-and-collect orders where customers choose to pay on site now remain visible in the Point of Sale until payment is collected. This prevents orders from being incorrectly treated as already paid online and ensures cashiers can settle the full amount at pickup.
Original PR description
When a customer orders online with click and collect and chooses to pay on site, the order is marked as paid on the eCommerce even though no money has been received yet. Because of this, the order was considered fully paid and no longer showed up in the Point of Sale, so the cashier had no way to settle it and collect the payment when the customer came to pick up the goods. Orders paid on site now stay visible in the Point of Sale with the full amount left to pay. Task-6466932
This update corrects how bullet and numbered lists are displayed when users turn large heading text into a list in the HTML editor. It prevents list markers from shifting too far left, improving document layout consistency and visual quality.
Original PR description
Problem: Creating a list on a header block with large font size content causes the list marker/bullet to overflow to the left. Cause: `ListPlugin.blockToList()` wrapped block elements into a list without invoking `this.adjustListPadding(list)`, leaving the list padding unadjusted for larger font sizes. Solution: Call `this.adjustListPadding(list)` in `blockToList` so that proper inline padding is set based on the list item content font size. Steps to reproduce: - Create a header block (e.g. Header 4). - Change the font size of the header content to be bigger. - Apply a list on the content. => Observe that the list marker overflows to the left. opw-6542903 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents French e-invoicing tests from failing when an optional French PDP component is not installed. It keeps automated validation reliable across setups without changing business functionality for users.
Original PR description
### Issue: Tests in `TestCIIFR` fail when run without `l10n_fr_pdp` installed The expected XML files contain `PMT`, `PMD` and `AAB` notes that are only generated when `l10n_fr_pdp` is installed ### Cause: The note generation for FR e-invoicing lives in `l10n_fr_pdp` When it is not installed, the notes are absent from the generated XML but still present in the expected test files When `l10n_fr_pdp` is not installed, the expected tree is stripped of `PMT`, `PMD` and `AAB` notes before comparison ### Steps to reproduce: - Install `l10n_fr_account` without `l10n_fr_pdp` - Run `TestCIIFR` from `l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py` Before the fix, the affected tests fail on the `cbc:Note` comparison runbot-945999 Forward-Port-Of: odoo/odoo#284661
This fixes Malaysian MyInvois consolidated invoicing so point-of-sale orders are grouped by PoS configuration before invoices are created. Businesses with multiple PoS configurations should now receive the expected consolidated invoice grouping instead of unexpectedly generating hundreds of small documents.
Original PR description
#### Description of the issue/feature this PR addresses: When consolidating PoS orders for MyInvois, about one Consolidated Invoice is expected per PoS config(although could be more than 1). When…
#### Description of the issue/feature this PR addresses:
When consolidating PoS orders for MyInvois, about one Consolidated Invoice is expected per PoS config(although could be more than 1). When several configs sell during the same period, the wizard instead creates hundreds of documents each covering only around 100 orders.
#### Current behavior before PR:
_split_pos_orders_in_lines groups the session orders with itertools.groupby keyed on config_id, but the recordset is sorted using pos.order._order ("date_order desc, name desc, id desc"), which never orders by config. Since groupby only groups consecutive equal keys, orders of concurrently selling configs interleave and each config change starts a new group, appending an extra line. The wizard creates one document per MAX_LINE_COUNT_PER_INVOICE lines, so the spurious lines multiply the documents.
#### Desired behavior after PR is merged:
The session orders are sorted by config before grouping, so each config forms a single group and yields one line per continuous receipt range, giving one Consolidated Invoice per config. Sorting is stable, so date ordering within a config and the existing receipt continuity check are unchanged.
Ticket [link](https://www.odoo.com/odoo/project.task/6448122)
opw-6448122Restores the standard default profile for French electronic invoicing after a previous change selected a non-mandatory extended profile. This helps more users send and receive French invoices successfully without needing support for the extended format.
Original PR description
**PROBLEM** https://github.com/odoo/odoo/pull/281717 changed the pdp default profile to be the french extended profile. However, this profile is not mandatory, so most user can't send/receive it. This prevent user from sending french invoices. opw-6420924 Forward-Port-Of: odoo/odoo#288232
The appointment pages list in the Website app now opens the correct editing screen when users choose Edit from a kanban card. This removes a dead action and helps staff update appointment page settings without switching views or using a workaround.
Original PR description
Steps to reproduce: 1. Install website_appointment 2. Website > site > appointment > kanban view 3. On a record, open the dropdown menu and click Edit. Issue: The Edit button does nothing. Cause: The Website appointment pages action only defines list,kanban views. When the kanban Edit action is triggered, the web client tries to switch to a form view, but no form view is available in the action, so nothing happens. Solution: Add the `appointment_type_view_form` to the Website appointment pages action and include form in its view_mode, so kanban Edit can open the selected appointment type correctly. opw-6197438 Forward-Port-Of: odoo/enterprise#117381
Belgian payroll now uses the employee's actual departure date when preparing holiday departure attestations instead of the notice period end date. This helps ensure the documents and related payroll calculations reflect the correct employment end date.
Original PR description
- Use `departure_date` instead of `end_notice_period` Task: 5391142
Starshipit delivery rates now handle incomplete wallet addresses without stopping checkout. If Starshipit cannot quote a rate because details like the street are missing, other delivery options can still appear so customers can complete Apple Pay or Google Pay purchases.
Original PR description
#### Description of the issue/feature this PR addresses: Rating a Starshipit shipment for an incomplete delivery address makes the whole rating loop fail instead of skipping that carrier. This breaks…
#### Description of the issue/feature this PR addresses:
Rating a Starshipit shipment for an incomplete delivery address makes the whole rating loop fail instead of skipping that carrier. This breaks express checkout (Apple Pay / Google Pay), where the wallet only discloses a partial address before authorization: the customer gets "update postal address" and cannot pay, while manual checkout works.
#### Current behavior before PR:
delivery_starshipit is the only connector that rates a shipment without validating the addresses first, and it ignores the express_checkout_partial_delivery_address context key set by website_sale. The empty street is sent to /api/rates, which answers 200 with {"success": false, "errors": [{"details": "street parameter value is required"}]}, and _send_request turns that into a UserError. As starshipit_rate_shipment lets it propagate, it aborts the rating of every carrier instead of only this one, so no delivery method reaches the wallet. The same happens for errors only the api can report, such as invalid credentials.
#### Desired behavior after PR is merged:
Both the recipient and the warehouse address are validated before the api is called, as the other connectors already do, and the message is returned as an unsuccessful rate rather than raised. Starshipit requires the street, so a streetless address is still refused, but with a neutral message in the express checkout flow since the customer cannot complete it before paying. The Starshipit carrier is simply not proposed among the wallet's options, the other carriers are rated normally, and express checkout completes.
opw-6393393
Forward-Port-Of: odoo/enterprise#129640
Forward-Port-Of: odoo/enterprise#125117Users who enabled browser notifications could receive two alerts for the same mention when their Odoo tab was open but not active. This fix ensures Odoo only shows the appropriate notification, reducing confusion and notification noise.
Original PR description
Steps to reproduce: - Grant browser notification permission (subscribes to web push). - Set user A's `notification_type` to `inbox` in preferences and reload. - Keep A's tab open but unfocused. - As user B, post an @-mention on a non-channel chatter record targeting A. A receives two alerts: the JS one from the `mail.message/inbox` bus event and the native notification from web push. `OutOfFocusService.notify()` gated JS skip on `message.thread.model`, but the server never sends `mail.thread`, so chatter fell through. Replace it with a `message_type` + `!isSelfAuthored` filter mirroring the server side `_notify_get_recipients_for_extra_notifications`. Also swap the `isInbox` SW handshake workaround added by [1] when the user is busy. [1]: https://github.com/odoo/odoo/pull/232431
This fix prevents older approved leaves from being reprocessed when a new employee version is created or updated. It helps keep historical leave records stable and ensures only leave relevant to the current employee version is considered.
Original PR description
Description of the issue/feature this PR addresses: When creating or updating an employee version, hr_holidays looks for leaves from the contract start date. If the contract started before the current employee version, this can include validated leaves belonging to older versions and older working schedules. This is a backport of the PR: https://github.com/odoo/odoo/pull/264997 Current behavior before PR: Creating a new employee version may process historical validated leaves that ended before the new version starts. Those leaves can be moved back to approval or recomputed even though they are not relevant to the new version. Desired behavior after PR is merged: Only leaves relevant to the employee version being created or updated are processed. Leaves ending before the effective version start date are ignored and remain unchanged. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes incorrect test setup data across several Odoo apps by tightening validation of mocked field definitions and allowed selection values. It helps catch errors earlier during testing, reducing the risk of hidden issues reaching users.
Original PR description
- https://github.com/odoo/odoo/pull/256814 See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#131914
This fixes Factur-X invoice exports so they use the company or parent contact name when an invoice address has no name of its own. This helps prevent rejected e-invoices caused by missing buyer name information.
Original PR description
Description of the issue/feature this PR addresses: The Factur-X Refactoring (https://github.com/odoo/odoo/pull/284123) introduced an error that can result in the generated XMLs not being compliant due to the partner name being missing (BT-44) Current behavior before PR: When a contact has an invoicing address without a name set, then the new Factur-X generation does not fall back to the parent name, leaving the corresponding XML entry empty, which is therefore pruned, leaving the Factur-X without a BT-44 and thus failing BR-07 Desired behavior after PR is merged: The new generation method uses the same data source and fallback method as the old Factur-X generator, pulling the `display_name` of the `commercial_partner_id` if the address doesn't have a `name`. This greatly reduces the chance of a generated Factur-X failing BR-07. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#289498
This update fixes several issues in Odoo's automated web testing tools and test data, making test results clearer and more reliable. It helps development teams catch problems earlier and reduces the risk of unstable or misleading test outcomes across apps like Accounting, Calendar, Live Chat, Mail, and Point of Sale.
Original PR description
- https://github.com/odoo/enterprise/pull/131914 Various Hoot/web tests fixes. See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256814
Fixes Peruvian PLE sales and purchase ledgers so ISC tax is not incorrectly included in taxable base amounts or counted multiple times. This keeps ledger declarations aligned with electronic invoices sent to SUNAT and prevents overstated tax reporting.
Original PR description
When a tax affects the base of the subsequent ones — `include_base_amount`, which is how the ISC is set up in Peru — Odoo links the tax line it generates to those subsequent taxes, so the ISC tax…
When a tax affects the base of the subsequent ones — `include_base_amount`, which is how the ISC is set up in Peru — Odoo links the tax line it generates to those subsequent taxes, so the ISC tax line ends up carrying the IGV in its `tax_ids`. The PLE ledgers build their base columns by summing every move line related to a given tax group, so that ISC tax line was added to `base_igv` as well and the ISC ended up reported twice: once inside the taxable base and once in its own column. A line of 1000.00 with 100.00 of ISC and 198.00 of IGV was declared with a base of 1100.00 instead of 1000.00. `l10n_pe_edi` already subtracts the ISC from the `cbc:TaxableAmount` of the CPE sent to SUNAT, so the electronic invoice and the ledger disagreed on the very same document. ### What changed The base taxes are now joined only for base lines, instead of repeating the condition on each of the twelve `base_*` columns. Besides keeping tax lines out of the base columns, this also stops a tax line from being counted once per subsequent tax: an ISC followed by both the IGV and the ICBPER produced two rows in the join, and its own column was reported twice. ### Scope `_get_ple_report_data` is shared, so this covers the sales ledger 14.1 and the purchase ledgers 8.1 and 8.2. A configuration where no tax affects the base of a later one is unaffected: the standard Peruvian chart template has no purchase ISC, so ordinary IGV-only purchases report exactly the same values as before. ### Tests - `test_sale_report_isc_base`: an ISC preceding the IGV, on an invoice and on a credit note; the base column excludes the ISC. - `test_sale_report_isc_reported_once`: an ISC followed by the IGV and the ICBPER is reported once. Both fail without the fix. Supersedes odoo/enterprise#128300, which targeted 19.0. Forward-Port-Of: odoo/enterprise#131475