Friday, September 4, 2026
100 changes · master
New functionality added to Odoo
Website editors can now showcase blog posts and upcoming events in dynamic carousel layouts, making content-heavy pages easier to browse without taking up too much space. The update also reuses shared filtering logic so future changes are easier to maintain across similar snippets.
Original PR description
### [IMP] website, website_blog, *: add dynamic blog posts carousel snippet *: website_sale This PR adds a new "Blog Posts Carousel" snippet that displays blog posts in a paginated carousel layout,…
### [IMP] website, website_blog, *: add dynamic blog posts carousel snippet *: website_sale This PR adds a new "Blog Posts Carousel" snippet that displays blog posts in a paginated carousel layout, showing multiple posts per slide. This fills a gap where the existing grid/list snippets were the only options, making it difficult to showcase many posts compactly without overwhelming the page. How to use: 1. Enter Edit mode 2. Click on Blogs 3. Select the one with "Dynamic Carousel" label The search domain logic (filtering by blog ID) is extracted into a BlogPostsMixin so it is shared between the existing BlogPosts and the new BlogPostsCarousel interaction, keeping future domain changes in one place. The .carousel-item vertical padding rule is moved from s_dynamic_snippet_products to s_dynamic_snippet_carousel so it applies to all carousel-based snippets, not just products. task-5090459 ### [IMP] website_event: add dynamic events carousel snippet This PR adds a new "Events Carousel" snippet that displays upcoming events in a paginated carousel layout. The existing snippets only supported grid and list layouts, leaving no compact option for content-dense pages where horizontal space is limited. How to use: 1. Enter Edit mode 2. Click on Events 3. Select the one with "Dynamic Carousel" label The tag filtering domain logic is extracted into an EventsMixin shared between the existing Events and the new EventsCarousel interaction, ensuring any future domain changes only need to happen in one place. Enterprise PR - https://github.com/odoo/enterprise/pull/101478 task-[5090459](https://www.odoo.com/odoo/project/974/tasks/5090459)
Adds support for connecting a weighing scale directly through the browser for quality control checks. This helps operators capture weights automatically for kilogram-based checks, reducing manual entry and improving accuracy on the shop floor.
Original PR description
Community PR: <https://github.com/odoo/odoo/pull/284985> This commit adds a new bridge module to allow measuring the weight at quality control points using a scale connected directly via Web Serial. Once a scale is connected, it will be used automatically whenever a quality check with unit 'kg' is performed. task-6485996
Website editors can now add an Appointments Carousel that presents appointment types in a compact, paginated layout. This makes it easier to promote many bookable services on a page without overwhelming visitors, while preserving the existing filtering options.
Original PR description
This PR adds a new "Appointments Carousel" snippet that displays appointment types in a paginated carousel layout. The existing snippets only supported card, picture, and list layouts, with no compact option for showcasing many appointment types without overwhelming the page. How to use: 1. Enter Edit mode 2. Click on Appointments 3. Select the one with "Dynamic Carousel" label The filter domain logic (by type, users, resources, and names) is extracted into an AppointmentsMixin shared between the existing AppointmentsListSnippet and the new AppointmentsCarousel, keeping future domain changes in one place. Community PR - https://github.com/odoo/odoo/pull/238099 task-[5090459](https://www.odoo.com/odoo/project/974/tasks/5090459)
Enhancements to existing features
French accounting localization now treats Monaco as part of the EU VAT group while preserving a separate EU group that excludes Monaco. New localization packages were also added for French overseas territories, helping businesses apply the right local accounting and tax setup in those regions.
Original PR description
Resolved issues and error corrections
This update fixes reliability issues in guided onboarding tours, especially when tours switch modes, open the editor, or move across pages. Users should experience fewer interrupted or incorrectly validated tour steps in areas such as website forum onboarding.
Original PR description
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
Code cleanup and technical improvements
Odoo now stores only the user's active search choices in page links, rather than the full internal view state. This makes navigation links lighter, less cluttered, and easier to restore reliably when reloading pages or working offline.
Original PR description
Only the search performed by the user (the active facets) needs to end up in the url, not the whole internal state of the view. Pushing the complete state was heavier than necessary and leaked more detail into the url than a search really is. A lightweight representation of "what the user searched for" already existed, introduced for reapplying a search when working offline. Reusing it here avoids maintaining two different ways to describe a search, and pushed us to make that representation a bit more robust so it can be trusted for both purposes. Finally, deciding what belongs in the url is the search model's responsibility, not the action service's: the model is the one that actually knows what a facet is. Moving that decision there removes an indirection the action service had no real reason to carry.
[[IMP] l10n_fr,l10n_fr_account: European union vat](https://github.com/odoo/odoo/commit/9870a204bb64bdf8a11e433eef3a9e9f8f3fad86)
This commit will add Monaco to the European union vat country group and
create a new country group that is the same than the european union but
without monaco
task-6321652
[[ADD] l10n_{gf,gp,mq,re,yt}: new localization](https://github.com/odoo/odoo/commit/58518e1d7df773a70c595bd3842a33ac6be611bb)
Like Monaco, we need to add new localization package for
the drom countries
task-6321652
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#284404Sales cash rounding is easier to manage, with editable and archivable rounding methods, clearer handling when rounding has no effect, and better default profit and loss account setup. These changes reduce configuration mistakes and make invoice rounding behavior more transparent for users.
Original PR description
This commit is a followup of the work done in this PR https://github.com/odoo/odoo/pull/283020 It adds the following improvements: 1) Show the icon to remove the cash rounding in case of biggest_tax strategy before saving the record 2) set profit/loss default accounts on the company 3) Fix unit test (partner category count says 3 while actually 2 are included) 4) when the tax group groups 0% taxes, should never be != 0, and notify the user that no rounding took effect 5) Make rounding methods archivable 6) Make the rounding methods list view editable top, and support multi edit 7) Compute new rounding method records names 8) Restrict rounding profit/loss accounts to internal groups of income/expense task-6501537
Users can now open the full composer while editing existing Chatter messages or notes, with the current content carried over. This makes it easier to use richer editing options such as formatting, attachments, and mentions while ensuring the original message is updated rather than duplicated.
Original PR description
**Description of the issue/feature this PR addresses:** ---------------------------------------------- When editing messages or log notes from the Chatter, users are limited to the inline composer…
**Description of the issue/feature this PR addresses:** ---------------------------------------------- When editing messages or log notes from the Chatter, users are limited to the inline composer and cannot switch to the full composer. This restricts their ability to perform advanced editing (rich formatting, attachments, mentions handling, etc.) once edit mode is initiated. **Current behavior before PR:** ---------------------------------------------- - No option is available to open the full composer while editing - Users must either continue with limited inline editing or discard changes - Full composer can only be used when creating new messages, not editing existing ones **Desired behavior after PR is merged:** ---------------------------------------------- - "Open Full Composer" action is available while editing messages/notes - Full composer opens with existing message content pre-filled - Attachments, mentions, and formatting are preserved during transition - Saving from full composer updates the original message instead of creating a new one - Existing behavior for new messages remains unchanged Related Enterprise PR: https://github.com/odoo/enterprise/pull/126622 Task-6015654 ---------------------------------------------- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Indian reporting now includes audit checks for Form 26, helping businesses review required tax return information more consistently. This improves compliance support by flagging relevant items during the reporting process.
Original PR description
This PR adds audit checks for Form 26 in India. task-6182154 Community PR - https://github.com/odoo/odoo/pull/286430
Users can now open the full message composer when editing chatter messages, giving them a more complete editing experience. The underlying context naming was also aligned with existing mail conventions, improving consistency for future maintenance.
Original PR description
The clicked_on_full_composer context key is renamed tomail_full_composer. The new name follows the naming of the other mail context keys. Related Community PR: https://github.com/odoo/odoo/pull/256870 task-6015654
The timesheet assistant now gives more importance to recent activity matches, so suggestions adapt faster when people move to new projects or tasks. It also cleans up outdated local history and reduces unnecessary lookups, making suggestions more relevant and efficient.
Original PR description
Previously, the timesheet assistant's local configuration matched activities to projects/tasks using a strict historical tally. A match made 6 months ago carried the exact same mathematical weight as a match made today. This caused old habits to stubbornly override new assignments. This commit introduces an exponential time-decay algorithm to the frequency computation: - Matches are now weighted based on their `datetime` stamp. - The weight drops by 50% every 5 days (half-life algorithm). - Recent timesheet entries quickly accumulate enough fractional votes to surpass the decayed votes of older, outdated projects. in this commit i updated the matching of local rules to be case sensitive for better matching (eg. "Meeting with client" is the same as "meeting with client") Task: 6267713 Forward-Port-Of: odoo/enterprise#129997 Forward-Port-Of: odoo/enterprise#124669
The AI update process now blocks empty selection criteria in all cases, including flows that ask for confirmation. This reduces the risk of an AI mistake changing every record unintentionally, while still allowing deliberate all-record updates when an explicit matching condition is provided.
Original PR description
The llm can hallucinate and call the update tool with an empty domain which will result in performing the updates on all the records of a certain model. To prevent hallucinations and at the same time allow the llm to update all the records if it actually needs to, an error is thrown if the domain is empty stating that updating with an empty domain is prohibited and a domain that matches all records should be used instead. This will make the llm conscious about passing a domain that actually matches all records and not just use an empty domain. Note: the update tool was already throwing an error if the domain is empty but only if tool_request_confirmation is False. If the confirmation is True, which is the case in mcp, the domain would be accepted and the updates would go through. Forward-Port-Of: odoo/enterprise#130349
Manufacturing now supports packing finished products as part of the production workflow, aligning manufacturing with existing stock packing processes. This helps teams prepare goods for shipping or storage more efficiently, though some automated print actions from detailed stock move lines are not yet triggered and are planned for a later update.
Original PR description
In this commit, we introduce the basic functionality of put in pack in MRP, as it makes sense to pack the manufactured product when needed. Later improvements to be done in order to tweak the behaviour and a `Put in Pack` action might be added on the operation directly. For now, the `Put in Pack` action in stock move lines don't trigger the hooks correctly and it should be fixed in another PR for 19.0 (since this PR targets V20), This means that on putting on pack from the stock move line operations list, f you have the print on "Put on Pack" or similar on, it won't be triggered. Task: 6247512 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Public holidays now appear in Discuss the same way as regular time off, using each employee's company calendar when they have no approved leave. This helps colleagues better understand availability by showing banners, status, and names with an expected return date when relevant.
Original PR description
**Public holidays** are now treated like regular out-of-office status in Discuss. and the holiday is computed on hr.employee from the employee’s company calendar (only when they have no validated leave). The Discuss banner, IM status, and formatted display name show “back on XXX” when appropriate, task-2377854 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Online shop filtering now refreshes key page controls consistently, including breadcrumbs, sorting, search, category links, and floating filter controls. This gives shoppers clearer navigation and prevents old filter options or reset links from lingering after they refine product results.
Original PR description
Applying shop filters without a full page reload left several server-rendered regions stuck at their page-load state. The products header (breadcrumb, sort, filmstrip, search) and the floating bar are now refreshed on each update, so their keep() links are rebuilt against the current filters rather than the ones present at page load. Searching now targets a clean /shop, and the category links, "All Products" and "Clear Filters" share one reset dict, so searching or selecting a category clears the active filters consistently. The on-sale and in-stock ribbon pills now go through the same async flow as the other filters instead of forcing a full page reload. Also fix the collapsible category tree, whose toggle collapsed its own header: after the title became a div, the inheriting xpath matched the title instead of the category list. task-6297769 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Manufacturing barcode workflows now include Put in Pack actions, letting users package finished goods or by-products directly during barcode operations. Manufacturing operation settings also expose the related packaging options, making package handling and package PDF printing easier to configure and use.
Original PR description
After https://github.com/odoo/odoo/pull/267679 and the introduction of `Put in Pack` in MRP. We can now show put in pack options in the configuration of MRP. Task: 6247512
Employee group insurance fields now appear only when group insurance is selected, making employee records cleaner and easier to manage. Group insurance employee contributions are also included in Belgian tax reports and used to reduce withholding taxes where applicable.
Original PR description
Now, the group insurance fields are only displayed on the employee's form when the Group Insurance field is checked. Also changed the _onchange methods for hospital and ambulatory insurances to _compute methods. Added group insurance employee contribution to 281.10 and 281.20 reports. Added reduction for the withholding taxes based on 30% of the personnal employee contribution to the group insurance. task-6263912
The fleet stock planning view now shows recently used docks and responsible people even when they have no batches scheduled in the current period. This makes drag-and-drop planning easier by keeping common planning groups visible and ready to use.
Original PR description
To improve the drag and drop planning inside the gantt view, 'recent' docks and responsibles groups are displayed, even if they don't have any batch for the current period. Task-6294586
Customer reminder information is now handled more accurately per company, reducing confusion in multi-company setups. The update also standardizes report file naming and streamlines how follow-up responsibilities are selected, making reminder workflows more reliable.
Original PR description
- Make last_reminder computed per company. - Add get_default_partner_report_filename as a separate function for renaming the Open Items report instead of doing it manually. - Pass the follow-up line to _get_followup_responsible instead of recomputing it from the partner field in _execute_followup_partner. task-6401457
Quality checks are now created more efficiently when stock move lines are created or updated. This reduces database work for large warehouse operations with category-based quality rules, improving performance without changing user workflows.
Original PR description
Description ----------- Creating or updating stock move lines calls `_create_check()` to find the quality control points applicable to each detailed operation. Category-scoped points previously…
Description
-----------
Creating or updating stock move lines calls `_create_check()` to find the quality control points applicable to each detailed operation.
Category-scoped points previously searched for products once per quality point and category. The method then performed another `quality. point` search for every move line, although those point ids came from the recordset fetched at the beginning of the method.
This commit resolves all category-scoped products in one search and reuse the already-fetched quality points.
Benchmark
---------
```xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--
Reproduces the two query-scaling paths in stock.move.line._create_check:
category-scoped control points and a large batched move-line creation.
The final job is the benchmark target. With the former implementation,
its query count grew with both the control-point/category pairs and the
1,000 move lines. Use the scale option to increase only the move-line count.
-->
<record id="benchmark_move_line_quality_checks" model="populate.blueprint">
<field name="name">Benchmark: Move Line Quality Checks</field>
<field name="definition_xml" type="xml">
<create model="product.category" count="20" id="quality_parent_categories" scale="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="name" eval="f'Quality Benchmark Category {counter:02}'"/>
</create>
<create model="product.category" count="100" id="quality_child_categories" scale="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="name" eval="f'Quality Benchmark Subcategory {counter:03}'"/>
<field name="parent_id" ref="quality_parent_categories"/>
</create>
<create model="product.product" count="100" id="quality_products" scale="False" context="{'tracking_disable': True}">
<value name="counter" generator="misc.counter" start="1"/>
<field name="name" eval="f'Quality Benchmark Product {counter:03}'"/>
<field name="type" eval="'consu'"/>
<field name="is_storable" eval="True"/>
<field name="tracking" eval="'none'"/>
<field name="categ_id" ref="quality_child_categories"/>
</create>
<create model="quality.point" count="20" id="category_quality_points" scale="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="title" eval="f'Benchmark Category Control Point {counter:02}'"/>
<field name="apply_to" eval="'categories'"/>
<field name="product_category_ids" ref="quality_parent_categories" count="5"/>
<field name="picking_type_ids" eval="[Command.set([env.ref('stock.picking_type_in').id])]"/>
<field name="measure_on" eval="'move_line'"/>
<field name="measure_frequency_type" eval="'all'"/>
<field name="test_type_id" eval="env.ref('quality_control.test_type_passfail').id"/>
</create>
<create model="stock.picking" count="1" id="quality_picking" scale="False">
<field name="picking_type_id" eval="env.ref('stock.picking_type_in').id"/>
<field name="location_id" eval="env.ref('stock.stock_location_suppliers').id"/>
<field name="location_dest_id" eval="env.ref('stock.stock_location_stock').id"/>
<field name="origin" eval="'Quality control performance benchmark'"/>
</create>
<create model="stock.move" count="1000" id="quality_moves" scale="False" context="{'tracking_disable': True}">
<field name="picking_id" ref="quality_picking"/>
<field name="product_id" ref="quality_products"/>
<field name="product_uom_qty" eval="1.0"/>
<field name="uom_id" eval="env['product.product'].browse(product_id).uom_id.id"/>
<field name="location_id" eval="env.ref('stock.stock_location_suppliers').id"/>
<field name="location_dest_id" eval="env.ref('stock.stock_location_stock').id"/>
</create>
<create model="stock.move.line" count="1000" id="quality_move_lines" parallel="False">
<field name="move_id" ref="quality_moves"/>
<field name="product_id" eval="env['stock.move'].browse(move_id).product_id.id"/>
<field name="uom_id" eval="env['stock.move'].browse(move_id).uom_id.id"/>
<field name="quantity" eval="1.0"/>
<field name="location_id" eval="env.ref('stock.stock_location_suppliers').id"/>
<field name="location_dest_id" eval="env.ref('stock.stock_location_stock').id"/>
</create>
</field>
</record>
</odoo>
```
The populate benchmark creates 20 quality points linked to 5 categories each, resulting in 100 point/category relationships. It then creates 1,000 move lines in one batch, generating +-4.5k quality checks.
Products belong to child categories while quality points target their parents, this represents large stock operations with category-wide quality rules.
| | Before | After | Improvement |
|-------------|--------|-------|-------------|
| Time | 11.8s | 10.3s | 12.7% |
| Query count | 12487 | 10120 | 19.0% |
Reference
---------
task-6488454Point of Sale reports now show clearer session timing, including opening dates for active sessions and full start-to-end periods for closed sessions. The Sales Details report layout and multi-configuration names are also improved, making reports easier for managers to read and compare.
Original PR description
In this commit: - Display the opening date in the Session report when the session state is opened (X report). - Show the session period (start and end datetime) for closed sessions and sales details report. - Move the "Sales Details" title to the header for the Sales Details report for better layout consistency. - Improve config name display in multi-config reports by joining them in a single line. - Extend `pos.session` display_name to optionally include the session start datetime using the `display_start_at` context. Task: 5980266
Adds support for work injury sick leave calculations required under Hong Kong Cap. 282. This helps payroll and HR processes apply the correct leave amount after accounting for any post-accident earnings.
Original PR description
Implement support for work injury sick leaves as per the Cap. 282 from Hong Kong. These calculations require some special logic to handle the worked day line calculation; as the rate should be applied AFTER reducing the leave amount by any post accident earnings given to the employee. task-6517522 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Product images can now be assigned to shared attributes like color or size, so businesses no longer need to duplicate the same image across every variant. This makes product setup faster and ensures customers see the most relevant images on product pages while shop listings stay consistent.
Original PR description
## Before this commit: - Variant images had to be uploaded and managed separately for each product variant. - Even when multiple variants shared the same characteristics (e.g., the same image for all…
## Before this commit:
- Variant images had to be uploaded and managed separately for each product
variant.
- Even when multiple variants shared the same characteristics (e.g., the same
image for all sizes of a blue T-shirt), the image had to be duplicated across
variants.
## After this commit:
### 1. Image assignment using attribute values
- All images related to a product template, including variant specific images,
are now visible in the Extra Media section on both the template and its
variant forms.
- In the variant form, images that do not apply to the current variant (i.e.,
belonging to other variants) are visually muted.
- Each image can be linked to specific attribute values using a dropdown in the
kanban view.
- An image is applied to a variant only if the variant matches all attribute
values assigned to the image.
- If none of the values of attribute is selected on the image, then that
attribute will not affect the matching logic.
- In the product form view, images with attribute values are displayed first,
followed by images without attribute values.
This removes unnecessary duplication and simplifies variant image management.
### 2. Shop page behavior
- The primary image is always the template's main image.
- The secondary image is the second template image.
### 3. Product page behavior
- For the selected variant:
- Variant specific images are displayed first, with the first image serving as
the variant's main image.
- Template images without attribute values are displayed afterward.
### 4. Variant main image logic
- The first variant specific extra image is used as the variant's main image.
- If the user manually updates the variant's main image, the first variant
specific extra image is updated to match it. If no variant specific extra
image exists, a new one is created using the main image.
- If no variant specific extra images exist, the variant falls back to the
template's main image.
### 5. Template main image logic
- The first extra image is used as the template's main image.
- If the user manually updates the template's main image, the first extra image
is updated to match it. If no extra image exists, a new one is created using
the main image.
task-5405584The Discuss app interface has been updated to better match Odoo's Frost design, with clearer navigation, rounded search bars, stronger visual separation, and more readable message and settings areas. These changes improve day-to-day usability for teams using chat, channels, calls, and notifications without changing core workflows.
Original PR description
This commit adapts the overall UI of Discuss app to match with frost design. 1. Overall layout of Discuss app follows the floating box style of other Odoo screens 2. Messaging Menu style has been…
This commit adapts the overall UI of Discuss app to match with frost design. 1. Overall layout of Discuss app follows the floating box style of other Odoo screens 2. Messaging Menu style has been tweaked for improved readability: less muted items, better spacing, better visual of active item 3. Search bars are pilled-shaped to match search view. 4. Discuss app thread actions have style matching view switcher for panel actions. 5. Channel Member role iconography and action label has been changed to be more natural: crown icon, filled (owner) and unfilled (admin), "Set Member" for cleared downgrading of member role, consistent order of actions based on role hierarchy) 6. More visible border around core UI elements, like composer or message bubble. 7. Add People button has now an icon next to label. 8. Chat bubble message preview now is colored to match the message bubble color. 9. Notification Settings and Voice & Video Settings have their visual slightly simplified, with more spacing and less borders. 10. Call View actions and dropdowns no longer have weird gradient effect in simulated dark theme. 11. Call View Card's context menu stays open even when mouse leaving the card without any click away. Also improved color in this context menu. 12. Orange message bubble (mention to self) color has been tweaked to catch more the eye of the user. 13. Mentions style has been tweaked to pop a bit more. 14. Chat window outline is more separated from the background content through stronger border instead of shadow effect. 15. Add icons to Save as Template and Manage Templates in full composer. 16. Is typing text now shows member names in bold. 17. Autoresize, AutoresizeInput and DiscussContent's header have been fixed to properly follow the shrink-to-fit content aspect of header. <img width="2137" height="563" alt="Screenshot 2026-09-04 at 12 35 50" src="https://github.com/user-attachments/assets/81d317f2-bb56-4bdf-81d8-5aaa07e4eb4b" />
This update brings Odoo's spreadsheet component up to the latest version, improving day-to-day usability and visual consistency. Users should see smoother undo/redo behavior, more reliable area charts, better font handling on Linux, and colors that better match the rest of Odoo.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/bb7357a197 [REL] 19.5.0-alpha.16 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/bb7357a197 [REL] 19.5.0-alpha.16 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/75ceb30aaa [FIX] Fonts: fix linux font on css variable [Task: 6452045](https://www.odoo.com/odoo/2328/tasks/6452045) https://github.com/odoo/o-spreadsheet/commit/e8d059f38e [FIX] svg: remove unused commented SVGs [Task: 6317134](https://www.odoo.com/odoo/2328/tasks/6317134) https://github.com/odoo/o-spreadsheet/commit/60c210afc2 [IMP] viewports_store: do not jump viewport on UNDO/REDO [Task: 6481352](https://www.odoo.com/odoo/2328/tasks/6481352) https://github.com/odoo/o-spreadsheet/commit/537711e139 [FIX] chart: connect area charts through invalid values [Task: 6458796](https://www.odoo.com/odoo/2328/tasks/6458796) https://github.com/odoo/o-spreadsheet/commit/ea25ccfa4b [FIX] css: update global gray colors to match Odoo [Task: 6515003](https://www.odoo.com/odoo/2328/tasks/6515003) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
Point of Sale sales details now show both VAT-included and VAT-excluded prices. This makes it easier to distinguish gross sales from net sales amounts for clearer reporting and reconciliation.
Original PR description
We now display both prices VAT included and excluded in order to better distinguish the net sale amount. task-6517995
Users can now download German TSS fiscal exports directly from Odoo instead of using the external Fiskaly dashboard. The DSFinV-K export option has also been moved into the Fiscalization reporting area, making compliance exports easier to find and manage.
Original PR description
In this commit: - Introduce TSS export directly from Odoo, allowing users to download the TSS export without accessing the Fiskaly dashboard. - Move the DSFinV-K export from the Orders menu to the Fiscalization section of the Reporting menu for better organization. Task-6421997
Hong Kong payroll now supports work injury sick leave calculations required under Cap. 282. This helps employers calculate affected leave pay more accurately by accounting for post-accident earnings before applying the sick leave rate.
Original PR description
Implement support for work injury sick leaves as per the Cap. 282. These calculations require some special logic to handle the worked day line calculation; as the rate should be applied AFTER reducing the leave amount by any post accident earnings given to the employee. task-6517522
VoIP time condition periods now use clearer, localized time inputs and include an explicit All Day option. This makes period setup easier and prevents users from getting stuck when correcting invalid start or end times.
Original PR description
Time Condition periods used character fields with manual hour validation. The end time was also hidden until a start time was set, which could leave users unable to correct an invalid period when the start time was cleared. Use the standard float time widget for entering period boundaries and add an explicit All Day option. Keep both inputs on the same line as the option to avoid resizing the period dialog. Format period hours in the list with the same localized representation as the form inputs while preserving the HH:MM values expected by Wazo.
Belgian payroll now reports specific partially paid sick leave and work accident salary codes in the correct tax forms. This helps ensure these payments are taxed and counted correctly in annual and withholding tax declarations.
Original PR description
Some salaries for sick days and work accidents are partially paid (e.g., for short-term contract employees). These salaries should not be declared in forms 281.10 and 274.10, but in 281.18 and 274.18, as they are taxed differently (bonus withholding tax base). Following the previous implementation for LEAVE218, LEAVE219, and LEAVE229, this commit adds the missing work accident codes: - LEAVE271 - LEAVE272 Their categories have been updated to use `REPLACEMENT_REVENUE_BASE` instead of the standard remuneration and withholding bases. These codes are also added to the 281.18 sheet generation to properly compute the number of days. Task: 6482702
The Shopee Shops menu now guides users through the correct setup flow instead of leading to a dead end when creating a shop. Users can choose or create the needed Shopee account before authorizing a new shop, making setup clearer and easier.
Original PR description
The Shopee menu now exposes the shops instead of the accounts, as the shops are what the users work with. However, shops cannot be created manually: they only exist once they have been authorized on Shopee, which requires an account. Clicking "New" on an empty form was therefore a dead end. The "New" button of the Shops list now opens the account creation dialog when no account exists yet, and a wizard otherwise. From that wizard, the user selects the account on which to authorize the new shop, opens that account, browses all the accounts, or registers a new one. Accounts are also identified by their partner ID and API endpoint rather than by their name, so that the account to authorize the shop on can be told apart from the others. task-6487410
Half-day time off entries now appear as wide as full-day entries in the payroll Time Off calendar, making monthly reviews easier to scan. The selection count was also adjusted so selected days are reported correctly in both payroll and regular Time Off views.
Original PR description
In the Time Off step of a payrun, a half day time off was displayed half as wide as a full day one, making the gantt hard to read when scanning a month of time off. Pill width comes from the grid:…
In the Time Off step of a payrun, a half day time off was displayed half as wide as a full day one, making the gantt hard to read when scanning a month of time off. Pill width comes from the grid: the leave gantt declares precision="day:half", so a day spans two grid columns and a morning or afternoon time off covers only one of them. Override the precision to "day:full" on hr_leave_gantt_view_payroll, the primary view used by the payrun Time Off step and by the payroll time off action. A day is now a single column there, so half day and full day time off are displayed with the same width. The Time Off app gantt keeps its half day precision. The multi selection counted the selected grid cells and divided them by 2 to get a number of days. That only holds when a day is two cells, so with the new precision selecting a single day displayed "0.5 selected". Divide by the scale cellPart instead, which is the number of cells in a day: 2 with day:half, giving the same result as before, and 1 with day:full.
Repair orders now move through a shorter, clearer process by removing the unnecessary “Under Repair” step and the related “Start Repair” action. The final repaired status is now labeled “Done,” making the workflow easier for users to understand across repair, manufacturing repair, and point of sale repair scenarios.
Original PR description
This commit improves the repair workflow by: - Removing the `Under Repair` state and its related logic, as it provided no functional value beyond an intermediate state. - Removing the `Start Repair` action, which is no longer needed without the Under Repair state. - Renaming the `Repaired` state to `Done` and updating the tests to reflect the new repair workflow. Enterprise PR:- odoo/enterprise#123736 Upgrade PR:- odoo/upgrade#10739 TaskId-6361476
Manufacturing orders now show planning and status options only when they are relevant, especially when work orders are not used. This reduces confusion for users by hiding unnecessary buttons and filters, showing helpful messages, and keeping order statuses aligned with actual production progress.
Original PR description
*: project_mrp_account This improvement enhances the UX by: - Hiding the `Plan` button on the MO form when there are no work orders. - Ensure the MO state moves to `In Progress` only when the user clicks `Start`, when any work order is in `In Progress or Done` state, or when any component is `picked`. For MOs without work orders, the `To Close` state will never be used. - Display all relevant MO states by default, except the `Cancel` state. For MOs without work orders, the `To Close` state is also not displayed. - Showing a `toaster` when the user tries to plan an MO without work orders or an MO that is already planned. - Hide unnecessary actions, buttons, and filters when the `Work Orders` option is disabled in the settings. - Update the `To Plan` filter to include only MOs with work orders. - Update test cases to maintain consistency with the changes. Enterprise PR:- odoo/enterprise#129971 TaskId-6453469
Quality control point forms are now clearer and easier to complete, with better title handling, inline options, and helpful placeholders. Work order planning popovers now show key assignment details, helping users understand responsibilities faster.
Original PR description
*= mrp_workorder, helpdesk_repair The improvement includes: - Use a large title input, remove the `New` heading, and display the title next to the reference after creation. - Display the `Apply to` options inline and rename `Quantity` to `Lot` in the `Control per` field. - Improve the QCP `quick create` by using the entered text as the `title` instead of the `reference`. - Add placeholders to the title and failure location fields. - Display the work center and assignee of the work order in the planning popover - Update the tests to reflect the removal of the `Under Repair` state. Community PR:- odoo/odoo#275310 Upgrade PR:- odoo/upgrade#10739 TaskId-6361476
Payslip forms now show worked day totals that exclude unpaid time, while keeping existing payroll calculations intact. This gives payroll users a clearer view of paid work without disrupting FTE or amount calculations that depend on total worked time.
Original PR description
Rework the previous reverted commit to keep old fields used in FTE and amount of worked_days_line, and add 2 new computed field that only count paid time. Task-6205747
The Sign field list now shows who created each field and provides useful search and grouping options. This helps users quickly find signature fields by name or linked record, and organize them by creator, related record, or allowed user groups.
Original PR description
In the fields list view, there was no way to tell who added a field, and the search bar offered nothing to search or group on. The list now has a "Created by" column, fields can be searched by name or by the record they are linked to, and grouped by creator, linked record, or the groups allowed to use them. task-6480123
Payroll correction handling has been improved for standard payroll and Belgian payroll processes. This should help payroll teams process correction proofs of concept more reliably and reduce manual follow-up in affected payslip workflows.
The replenishment page now updates more smoothly when users close related pop-up wizards, instead of reloading the whole page each time. This reduces interruptions and helps warehouse teams continue working faster.
Original PR description
Do not refresh the entire orderpoint page every time a wizard is closed. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Belgian payroll declarations now calculate withholding-tax exemptions for eligible overtime hours and apply annual employee caps across the year. This improves compliance and reporting accuracy for companies using Belgian payroll, with updated XLS/PDF reporting and configuration for sector-specific limits.
Original PR description
Compute the withholding-tax exemption for overtime hours (natures 44–59) within the Belgian 274.XX declaration. Overtime worked-day lines are collected from validated payslips, bucketed by rate and sector (CP124/CP302/immovable work), and allocated to the appropriate nature codes while respecting each employee's annual hour cap. Hours already declared in earlier months of the same year are carried forward so the cap is enforced cumulatively. The exemption rate is stored as an updatable rule parameter. A white-cash-register flag on the company controls the higher cap for CP302 employees. New work-entry types, an XLS extra-hours sheet, a PDF section, and extended tests are included. Task Id: 5430988
The barcode app now opens serial and lot number generation in a dedicated screen instead of a pop-up dialog. This makes the workflow easier to use on mobile devices and keeps it more consistent with the rest of the barcode experience.
Original PR description
In the barcode app currently there's a dialog to generate serial/lot numbers. In this PR the dialog gets replaced by a view since it's easier for users on mobile devices. Task-5469775
The Project app search screens have been reorganized and cleaned up to make finding tasks and project information easier. This improves day-to-day navigation across Project-related areas such as timesheets, sales projects, sharing, and skills reporting without changing core business workflows.
Original PR description
In this commit, we refactor and clean the search view of Project. task-6516390
Call activities in the chatter now show the phone number from the related record, making it easier for users to place calls directly. Standard users get a normal phone link, while VoIP users continue to launch calls through the softphone, with shared handling across phone-related features.
Original PR description
[IMP] mail: display call activity phone numbers in the chatter
This was an enterprise VoIP feature: showing the phone number found on
the related record for call activities, in their chatter, and allowing
to call it by clicking on it.
This is now a standard feature. In standard, it is a `tel:` link. In
VoIP, it will still start a call through the softphone, as before.
[IMP] web, mail: merge activity/phone field code into 1 patchable point
The parent commit basically adds a VoIP feature to all call activities:
showing the phone number and being able to call it by clicking... this
is something the phone field widget also does. This commit factorizes
the code and makes it so VoIP can patch both points as an unique patch.
This also allows web_enterprise to patch both at the same time to add
the "Phone Install Dialog", suggesting VoIP installation to admins when
available.
task-6416875This update improves how Odoo retrieves bus notifications, especially when many notification channels are checked at once. It should reduce delays and server load for real-time updates while keeping behavior unchanged for users.
Original PR description
The `fetch_bus_notifications` method receives a dict mapping channels to be fetched to their `last_fetched_id`, producing several `channel_X = %s and id > %s` OR branches. Since [1], notifications…
The `fetch_bus_notifications` method receives a dict mapping channels to be fetched to their `last_fetched_id`, producing several `channel_X = %s and id > %s` OR branches. Since [1], notifications are fetched by DB, increasing the number of OR branches. Multiple ORs lead to O(n_row * n_groups) complexity as PostgreSQL has to walk through each OR branch for each row. A better approach is to JOIN the groups, which results in an O(n_row + n_groups) complexity: PostgreSQL first builds the hash table then can do a quick lookup for each row. This is an order-of-magnitude win for large batches, with a negligible difference for small ones. [1]: https://github.com/odoo/odoo/pull/278597 ------ Benchmark code, explain analyze for each case + plots: [benchmark.zip](https://github.com/user-attachments/files/31776770/benchmark.zip) Comparing old approach vs new approach with a growing amount of groups passed to `fetch_bus_notifications` on a 30k rows bus table. Median time over 50 run per batch. <img width="400" alt="bench_no_index" src="https://github.com/user-attachments/assets/e8c8dbee-ed1a-4941-867c-9ac0f251693a" />
Timesheet and invoice analytic records now automatically fill in missing sales and product details from related information. This makes reporting and grouping by product or sales order item more accurate for sales and service teams.
Original PR description
Analytic lines are missing two direct links that could be inferred from existing information: a timesheet has a sales order item and no product, and the analytic entries of an invoice have a product and no sales order item. With this PR, each side is filled in from what is already known Task-6486240
Customer lists now show reminder levels and include filters for overdue customers and reminder status. This helps teams quickly identify which customers need follow-up and prioritize collection activity.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/enterprise#128176 task-6462889
The CRM team switcher is easier to use, with simpler team names and a new default option to view all sales teams. It now appears whenever multiple sales teams use opportunities, making it more consistent and less dependent on configuration settings.
Original PR description
Purpose ======= Improve the usability of the crm team switcher. Specification ============= 1) Removing the "Pipeline" prefix from the switcher title. Users will get that it's the specified team…
Purpose ======= Improve the usability of the crm team switcher. Specification ============= 1) Removing the "Pipeline" prefix from the switcher title. Users will get that it's the specified team pipeline. 2) The "multi-memberships" settings shouldn't impact the switcher visibility anymore. The team switcher is now visible as soon as there's at least 2 teams using opportunities in the DB. NB: The teams actually displayed in the switcher could be a subset of those depending on the current user rights. 3) Adding an "All Sales Teams" option at the top of the team switcher acting as default. The switcher will fallback to it when no team has previously been selected or when the previously selected team cannot be found. Selecting it will remove the team id filter from the domain and remove the "default_team_id" context key from the context. With this scenario, the default team on record creation will be set by the crm.lead "team_id" field compute which will call "_get_default_team_id" from the sales_team module. Task-6485445
The Saudi Arabia accounting template has been reorganized to use a clearer six-digit account numbering structure. This makes the chart of accounts easier to navigate, removes duplicate or obsolete entries, and leaves room for future account additions without disrupting the order.
Original PR description
Restructure the Saudi Arabia chart of accounts to follow the standardized 6-digit numbering convention adopted across recent localizations (account group / subgroup / identifier), reorganizing account ordering for a more logical flow within each category and leaving spacing between codes so new accounts can be inserted later without disrupting the sequence. - Removed 9 obsolete/duplicate accounts (incl. duplicate Withholding Tax Payable) - Added 30 new leaf accounts and 19 new parent/header accounts - Re-mapped codes for all retained accounts while preserving their external ids Related PR: https://github.com/odoo/upgrade/pull/11120 https://github.com/odoo/enterprise/pull/128987 task-6390722
Phone numbers on activities are now available more broadly, so users can click to call even when VoIP is not installed. When VoIP is installed, the calling experience stays consistent across activity links and phone fields, with fixes for mobile and desktop phone link behavior.
Original PR description
See community PR for summary of the feature: https://github.com/odoo/odoo/pull/281277
task-6416875Customer lists now show reminder levels and include filters for overdue accounts and reminder status. This helps teams quickly prioritize follow-up work and identify customers needing attention without extra manual checks.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/odoo#282886 task-6462889
This fixes an issue where Odoo live chat embeds could block browser keyboard shortcuts such as Alt+D on external websites. The shortcut overlay now only intercepts the Alt key when there are actual Odoo shortcut hints to show, preserving normal browser behavior otherwise.
Original PR description
Description of the issue/feature this PR addresses: When `im_livechat` (or any page) boots the OWL web stack with no `[data-hotkey]` / `withOverlay` targets, pressing Alt still called…
Description of the issue/feature this PR addresses: When `im_livechat` (or any page) boots the OWL web stack with no `[data-hotkey]` / `withOverlay` targets, pressing Alt still called `preventDefault()` in `hotkey_service`. That blocked browser shortcuts such as Alt+D (focus address bar) on websites that embed the livechat scripts. See https://github.com/odoo/odoo/issues/267928 Current behavior before PR: - Pressing the overlay modifier (Alt, or Ctrl on macOS mapped to `"alt"`) always set `overlaysVisible = true` and called `preventDefault()`, even when there were no hotkeys to overlay. - A follow-up key (e.g. D while Alt is held) then also hit `preventDefault()` because overlays were considered visible. - Loading `/im_livechat/loader/...` + `assets_embed.js` on an external site therefore broke browser Alt shortcuts. Desired behavior after PR is merged: - `addHotkeyOverlays` returns whether overlays were actually displayed. - `preventDefault` on the overlay modifier runs only when there is at least one overlay target. - Pages with no hotkeys (typical livechat embed) leave browser shortcuts alone. - When hotkeys exist, Alt still shows overlays and cancels the default, same as before. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284145
This fixes a Point of Sale issue where the cash drawer could fail silently after refreshing the session because the default printer was not always remembered. The system now consistently saves the printer choice and prompts staff to select one when needed, helping checkout operations run smoothly.
Original PR description
Steps to reproduce: - Set only one printer with cashdrawer - Open PoS - Try to open the casdrawer => Silently won't open Issue: defaultPrinter was only saved in localstorage when various printer was set on the config. Fix: Now the printer is always saved in the localstorage so that when the pos is refresh, defaultPrinter is always set. Also when trying to open the cashdrawer if various printer are set in the config, make sure that the select default printer popup is shown. Task-6519466 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#285689
Product pages now keep multi-checkbox options unselected unless the shopper chooses them. This prevents confusing automatic selections and unwanted URL changes after refreshing a product page.
Original PR description
**Steps to reproduce:** 1. Create a product. 2. Add two attributes: multi-checkbox and any single-select type (e.g. Radio). 3. Publish the product and open its page on eCommerce. 4. Verify that no…
**Steps to reproduce:**
1. Create a product.
2. Add two attributes: multi-checkbox and any single-select type (e.g. Radio).
3. Publish the product and open its page on eCommerce.
4. Verify that no multi-checkbox option is selected by default.
5. Refresh the page twice.
**Issue:**
On the second page refresh, the first option of the multi-checkbox attribute is automatically selected, and the URL is updated with its parameter.
**Cause:**
- `_prepare_product_values` construct attribute combinations by mapping requested `attribute_values` query parameters line by line.
- When URL query parameters were present (e.g., set after the first refresh), the fallback logic `or ptal.product_template_value_ids.filtered('ptav_active')[:1]` treated unselected `multi_checkbox` attribute lines as missing required selections rather than empty selections, forcing them to default to their first active option.
**Fix:**
If no selection is provided for `multi_checkbox` line, return an empty recordset instead of falling back to the first active option.
opw-6494697
Forward-Port-Of: odoo/odoo#286492
Forward-Port-Of: odoo/odoo#284798The website shop editor no longer crashes when users choose the Grid catalog preset in the products design panel. This makes product page customization more reliable by preventing duplicate preset controls from conflicting internally.
Original PR description
Steps to reproduce: 1. Open the website editor on /shop. 2. Select the products grid and expand Products Page in the sidebar. 3. Click the Design button to open the Products Design sliding panel. 4. Click the 4th preset (Grid catalog) in the Preset picker. Before this pr: - The page crashed with a render loop error. After this pr: - presets can be picked normally, no crash. The preset list was being drawn twice at once, and both copies were registering under the same internal ID. Now each copy gets its ownnsuffixed ID, so they don't clash anymore. Also removed a redundant duplicate t-if on BuilderSelectItem already covered by its parent t-if. Also removed a dead label.translate="Presets" attribute that ProductsDesignPanelPresets never reads. opw-6478531 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284636
Users can now download spreadsheets with inserted images as Excel files without hitting an access error. The export process now uses the image access permissions already used in the web interface, so shared spreadsheet images remain available during export.
Original PR description
Since https://github.com/odoo/enterprise/pull/97488, the images are shared across spreadsheets and the attachments are no longer related to a specific record. Due to the management of the…
Since https://github.com/odoo/enterprise/pull/97488, the images are shared across spreadsheets and the attachments are no longer related to a specific record. Due to the management of the IrAttachment access rights, it means that those attachments are not accessible by anyone by default and we rely on the access_token to display them in the webclient. However, the method that builds the final xlsx file fetches the images from the server and did not use the access token, meaning that it could never access the attachment. Such situation raised a UserError that was caught by the webclient. How to reproduce: - As admin, create a spreadsheet and insert an image inside of it - try to download the spreadsheet as an xlsx file counterpart of https://github.com/odoo/enterprise/pull/126384 Task-6432724 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#285981 Forward-Port-Of: odoo/odoo#279788
Italian electronic invoices now list only the relevant linked down payment document instead of including unrelated past invoices or the current credit note. This helps avoid incorrect FatturaPA XML content and reduces confusion or compliance issues when issuing credit notes and final invoices.
Original PR description
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without…
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without reconciling it with anything else. - Generate the FatturaPA XML for credit note A. - Observe that DatiFattureCollegate lists both down payment invoice A and credit note A itself, instead of only down payment invoice A. On an order with several down payment invoices/credit notes over time, every one of them is listed instead of just the document the current credit note actually reverses. Cause of the issue: _l10n_it_edi_export_data built downpayment_moves from self.invoice_line_ids._get_downpayment_lines().move_id with no filtering. _get_downpayment_lines() (sale override) returns every invoice line ever created against the same down payment sale order line, across the whole life of the order, not just the invoice the current document actually relates to. The template renders DatiFattureCollegate for every one of those moves unconditionally, on top of the already-correct reversed_entry_id/reconciled_moves fallback, including the current document itself. Solution: Restrict downpayment_moves to lines with a negative price_subtotal, mirroring the existing down payment deduction-line detection a few lines above, and explicitly exclude the current document. This limits DatiFattureCollegate's down payment entries to the case they are meant for: a final invoice actually deducting a previously invoiced down payment. opw-6429716 Forward-Port-Of: odoo/odoo#285555 Forward-Port-Of: odoo/odoo#279938
This fixes an issue in Project Forecast where automatic planning could behave incorrectly when several roles were involved. Businesses using role-based forecasting should see more reliable planning results and fewer manual corrections.
Original PR description
task-6484707 Forward-Port-Of: odoo/enterprise#130166 Forward-Port-Of: odoo/enterprise#128541
DHL shipping in Odoo no longer automatically requests a package pickup. This removes the need to set pickup windows, reduces DHL configuration errors, and makes DHL behave more consistently with other shipping carriers.
Original PR description
Before this commit, the DHL REST module had the pick-up request set, which led to an unnatural use flow, needing to set up an scheduled date for pick-up, and several errors from DHL when these were not properly configured. This feature was unique to this module among the shipping carriers as we don't normally request pick-up for packages and leave it to user to decide how to do it. This commit removes the request for pick up and the logic that was needed to make it work. This will make the behavior consistent with other carriers in Odoo, and avoids the need to set scheduled pick-up windows and having to deal with unnecessary errors. opw-6148927 Forward-Port-Of: odoo/enterprise#123773
The event booth registration page now ignores outdated booth lists when visitors switch categories quickly. This prevents the page from showing booths from the wrong category and helps exhibitors complete booth selection without needing to reload.
Original PR description
Before this commit, the "Get A Booth" page of an event could keep listing the booths of the category checked on load after the visitor picks another one. Only a page reload brought the right ones…
Before this commit, the "Get A Booth" page of an event could keep listing the booths of the category checked on load after the visitor picks another one. Only a page reload brought the right ones back. The tour webooth_exhibitor_register timed out on that list: FAILED: [5/13] Tour webooth_exhibitor_register -> Step Choose Booth (trigger: .o_wbooth_booths div:contains(OpenWood Demonstrator 2) input:not(:visible)). TIMEOUT step failed to complete within 10000 ms. This happens because the page fetches the booths of the category checked on load, and clicking another category fetches its booths while that first answer is still on its way. The answer coming back last fills the list, and the widget caches it under the category active on arrival, so the booths of the first category land in the cache of the clicked one. This commit fixes the issue by caching an answer under the category it was asked for, and by filling the list only while that category is still the chosen one. https://runbot.odoo.com/odoo/error/110527 https://runbot.odoo.com/odoo/error/161834 Forward-Port-Of: odoo/odoo#286552 Forward-Port-Of: odoo/odoo#286266
French tax closing entries now correctly include VAT credits carried over from the previous period. This helps ensure VAT returns and journal entries comply with French accounting requirements and avoid missing credit balances.
Original PR description
### Issue before this commit: When generating a tax closing entry for France, the VAT credit carryover from the previous period is missing from the journal entry lines. ### Steps to reproduce the…
### Issue before this commit: When generating a tax closing entry for France, the VAT credit carryover from the previous period is missing from the journal entry lines. ### Steps to reproduce the issue: 1. Download Accounting and l10n_fr 2. Go to Vendor > Bills 3. Create a vendor bill with date in June and price > 0 (ex. 1000$) and the 20% G tax that produce a 200$ VAT tax 4. Go to Customers > Invoices 5. Create an invoice with price > 0 (ex. 9000$), the date in July and the 20% G tax that will produce a 1800$ VAT tax 6. Go to Tax Return and validate all opened months up to July and see that for June the balance is -200$ 9. Then go to View Entries of July using the 3 dots next to the "submit" button and see that there is no mentioning of the 200$ carry over of credit from the month before ### Cause of the issue: The issue stems from a structural change in the core code where the logic to evaluate and balance the tax receivable account was removed from the _add_tax_group_closing_items method. Consequently, the VAT closing entry now only processes the current period's taxes and there is no reporting of any carried-forward VAT credits. ### Reason to introduce the fix: French accounting rules strictly require the carried-forward VAT credit to be explicitly integrated into the current period's tax closing entry. opw-6438648 Forward-Port-Of: odoo/enterprise#130377 Forward-Port-Of: odoo/enterprise#129181
The French VAT reporting flow now checks for duplicate XML declarations before sending them to Aspone. This helps avoid rejected submissions and prevents unnecessary paid contacts with the service provider.
Original PR description
When sending an xml to aspone, they will check if a duplicate declaration exist, and if it's the case, then they will refuse it. Since contacting aspone cost us money we will block the sending before that. task-6420220 Forward-Port-Of: odoo/enterprise#130406 Forward-Port-Of: odoo/enterprise#127953
This fix prevents point-of-sale online payments from appearing twice in an order's payment history after confirmation. It also avoids an error when online self-ordering settings are unavailable, improving reliability for stores using POS online payments.
Original PR description
Also, bug noticed while working on task-6472188
Opening a project task in the customer portal could fail when the Timesheets app was not installed. The fix keeps time formatting available from the Project app itself, so portal task pages load reliably in more setups.
Original PR description
Before this commit, opening the portal page of a task in a database without hr_timesheet answers a 500:
AttributeError: 'account.analytic.line' object has no attribute
'_format_portal_hours'
This happens because project's portal_my_task_allocated_hours_template formats the allocated time with `_format_portal_hours`, a method hr_timesheet adds on `account.analytic.line`.
This commit moves that method to project, next to the template that calls it.
https://runbot.odoo.com/odoo/error/946944Belgian payroll meal voucher reports now correctly include postponed vouchers for employees assigned to a branch company. This prevents missing carryovers when payroll documents are created under a branch while the report is managed by the parent company.
Original PR description
Steps to reproduce: - Create a July payslip for Bernice and mark it as paid - Generate the meal voucher report for july - Create 3 time off for Bernice in July - Correct the payslip of July, in total…
Steps to reproduce: - Create a July payslip for Bernice and mark it as paid - Generate the meal voucher report for july - Create 3 time off for Bernice in July - Correct the payslip of July, in total we have 3 payslips now - Create an August payslip for Bernice - Create the meal voucher report for august Pay attention to: everything has been created under "My Belgian Company" while Bernice is in the branch "My Belgian Office" Current Behaviour: When the august mealvoucher report is not marked as done, the postponed shows 0 for Bernice. Expected Behavior: When the report is not marked as done, august should have 3 mealvoucher postponed for Bernice. The technical reasons behind this bug is that the SQL query for unreported payslips mapped to draft/ready reports filtered the payslips `company_id` on the mealvoucher `company_id`. But payslips for Bernice are under the branch "My Belgian Office" and the mealvoucher is created in "My belgian company". The SQL query should take company branches into account, same as in `_get_corrected_payslips`. task-6428719
Discount reward products are now created without automatically applying the company's default sales tax. This prevents discounts from being taxed incorrectly and keeps loyalty reward calculations aligned with expected untaxed discount behavior.
Original PR description
The reward's discount_line_product_id was created without an explicit taxes_id, so it silently fell back to the company's default sale tax (account_sale_tax_id) instead of staying untaxed like the rest of the program's discount/reward mechanics expect. Explicitly clear taxes_id when creating the discount product. task-6528122
Point of Sale screens now keep category images inside their buttons and use higher-resolution product images. This makes the sales interface cleaner and easier to recognize items quickly during checkout.
Original PR description
Category images overflowed their buttons because width-based ratio classes conflicted with the fixed button heights. Furthermore, product images appeared blurry because the low-resolution `image_128` was stretched to fit modern product cards. This ensures category images respect parent boundaries and fetches higher resolution product images. task-6479160
Time entries recorded outside an employee's normal schedule now behave correctly. Working time is counted as requested, while absences outside the schedule now show a clear message instead of silently recording zero time.
Original PR description
…dule Encoding a time type outside an employee's working schedule silently did nothing: 0 duration, no feedback, for both working time and absence types. Working time entries now count the requested time instead of 0. Absences still can't be taken outside the schedule, but now say so instead of silently doing nothing. Task-6501553
Accounting now correctly flags a draft journal entry as creating a numbering gap when a later entry is posted after it. This helps businesses maintain clearer audit trails and avoids unnoticed gaps in journal entry sequences.
Original PR description
To reproduce: * create and post 2 journal entries in the same journal * reset to draft the entry with the highest number * create and post a third entry in the same journal The draft entry is not flagged as having made a gap, because at the time of resetting it to draft, it was the last entry and therefore didn't really make a gap. 3 options to solve were considered: * flag all moves when reseting to draft even if they were the last of the chain * if we reset the last move of the sequence to draft, also remove its sequence and put it back to `/` * when posting, check if the previous number was draft. If it was the case, flag it. The last options was taken in this fix to avoid changing the previous behavior while fixing the current issue. Forward-Port-Of: odoo/odoo#286204
Creating a salary contract offer for Belgian employees no longer triggers an unintended Dimona update. This prevents incorrect or meaningless data from being sent to the government service during salary simulations.
Original PR description
### Issue When creating a salary contract offer (smart button 'New Offer' in the employee form), a dimona action can be triggered, sending no-sense value to the government api. ### Reproducing steps…
### Issue
When creating a salary contract offer (smart button 'New Offer' in the employee form), a dimona action can be triggered, sending no-sense value to the government api.
### Reproducing steps
I've only been able to observe that by placing a breakpoint in the `hr.version.write` of the `hr_version.py` of `l10n_be_hr_payroll`:
- Create a master (1 sept. 2026) database with demo data and the following modules installed: `l10n_be_hr_contract_salary,test_l10n_be_hr_payroll_account`
- Set the dimona environment of the demo belgian company to 'Sandbox'
- Go to the employee Laurie Poiret
- Set the current contract end date to 1 day before today
- Create a dimona IN declaration for the last contract of Laurie Poiret (the one that has just been modified) (ctrl + k / DIMONA)
- On employee form: click on 'Check Dimona' and apply the response, the state of the dimona should appear ('Done')
- Create a new contract ('New Contract' button on the employee payroll tab) and set it to today
- Change the contract start date to tomorrow so that the dimona state become 'In progress'
- Click on the smart button 'New Offer'
- See that the `hr.version.action_update_dimona` is executed (but it shouldn't)
task-6521357This fix prevents Belgian payroll payslips from creating duplicate input lines when a payslip is recalculated. It keeps payroll records cleaner and helps avoid incorrect or confusing payroll calculations after edits.
Original PR description
Steps to reproduce: - Create a December BE payslip so SIMPLE_DECEMBER/DOUBLE_DECEMBER_BASIC get seeded (or any struct feeding a BE-specific input code). - Trigger a recompute (edit date_from and save again). - Input line for that code is duplicated instead of replaced. _compute_input_line_ids's BE override only appended input lines, never unlinking the one it created last pass. Unlink existing line for a code before recreating it Task 6516068
Hourly and half-day time off requests on weekends or public holidays will no longer disappear without explanation. The system now sends these requests to the server so valid working-time entries are created and invalid absence requests return a clear message.
Original PR description
Issue: Creating an hourly or half-day (AM/PM) time off entry on a day flagged as a weekend/public holiday for the employee did nothing: no entry, no error. Cause: multiCreateRecords() pre-filtered out any such day client side via get_unusual_days(), regardless of the work entry type. Fix: Remove the client-side filter and let every selected day go through create(), with multi_leave_request set in the context so the server decides and notifies. working time entries get a real duration, absences get dropped with a clear message (see community PR). Task-6501553
Expense-created vendor receipts in Mexico now correctly include and display their CFDI XML attachment. This helps users access required tax documents directly from the bill and ensures these receipts can be checked with SAT services when needed.
Original PR description
**Current behavior:** Currently, account moves created from expenses (type receipt) don't include the XML when it is a CFDI. Causing users cannot see their attachment even though it is created on DB. https://docs.google.com/videos/d/1eFSAA-wvUzPDwIJDeiS2dkne94lGM7QBxRfjN3HxaLw/play **Versions:** 19+ **Fix:** Implementing a new helper to identify those moves that can actually hold a CFDI document (for now, all `is_invoice()` documents + vendor bill receipt, `in_receipt`), so now, we include 'in_receipts' in _compute_l10n_mx_edi_cfdi_state_and_attachment, _compute_l10n_mx_edi_document_ids, _compute_l10n_mx_edi_update_sat_needed and l10n_mx_edi_cfdi_try_sat. As these documents might need to fetch SAT services as well. Task-id: [6397080](https://www.odoo.com/odoo/project/49/tasks/6397080) Forward-Port-Of: odoo/enterprise#130045 Forward-Port-Of: odoo/enterprise#126493
Uploading a refund from bank transactions now creates a vendor credit note in the correct purchase journal instead of a customer credit note. This helps refunds reconcile properly against vendor payables and prevents accounting errors during bank reconciliation.
Original PR description
**Steps to reproduce:** * Install **Accounting** module. * Go to **Accounting → Bank → Transactions** (bank reconciliation widget). * Open a transaction with a **positive** amount (e.g. a vendor…
**Steps to reproduce:** * Install **Accounting** module. * Go to **Accounting → Bank → Transactions** (bank reconciliation widget). * Open a transaction with a **positive** amount (e.g. a vendor sending money back). * Click the three-dot menu on the transaction line and choose **Upload a Refund**. * Upload any document (XML or PDF). **Observed behavior:** * A **Customer Credit Note** (`out_refund`) is created in a **sale** journal instead of a **Vendor Credit Note** (`in_refund`) in a **purchase** journal. * The wrong document type means the reconciliation fails to link the refund against the correct payable account. **Cause:** * In `create_document_from_attachment` (`account_bank_statement.py`), the JS widget sends `type='sale'` in context when the transaction amount is positive (JS: `amount > 0 ? "sale" : "purchase"`). * The original code mapped `type='sale'` → `default_move_type='out_refund'` (customer credit note) and searched for a `sale` journal — both wrong. * Uploading from a bank statement is always a **vendor-side** operation: negative amount = vendor bill (`in_invoice`), positive amount = vendor refund (`in_refund`). The `type` context key from JS reflects transaction direction, not the accounting document type. * Additionally, `in_refund` is a purchase document; Odoo's `_check_journal_move_type` constraint raises a `ValidationError` if a purchase document is created in a non-purchase journal, meaning the old code would crash at the ORM level for the refund path. **Fix:** * Map `type='sale'` → `default_move_type='in_refund'` (vendor credit note) instead of `out_refund`. * Always search for a **purchase** journal regardless of the `type` context value, since both `in_invoice` and `in_refund` are purchase-side documents. opw-6468779 Forward-Port-Of: odoo/enterprise#129385 Forward-Port-Of: odoo/enterprise#127912
Odoo now calculates Mexican CFDI payment complements using the same rounded amounts that appear in the XML. This prevents valid USD invoices with MXN payments from being rejected by the tax certification provider with error CRP20268.
Original PR description
**Steps to reproduce:** * Install the **l10n_mx** localization. * Enable **USD** and fetch the latest currency exchange rate from the settings. * Configure **Quadrum** as the **PAC** in the settings.…
**Steps to reproduce:**
* Install the **l10n_mx** localization.
* Enable **USD** and fetch the latest currency exchange rate from the settings.
* Configure **Quadrum** as the **PAC** in the settings.
* Create and sign a USD invoice with **16% IVA** (e.g. 4,500 USD + 720 USD tax = 5,220 USD total).
* Send the invoice to **CFDI**.
* Register a **partial MXN payment** that does not convert to a round USD amount (e.g. 30,000 MXN = 1,762.45 USD).
* Send the payment complement to the PAC by clicking **Update Payments** on the invoice.
**Observed behavior:**
The PAC rejects the CFDI with error **CRP20268**:
> El campo BaseP que corresponde a Traslado, no es igual a la suma de
> los importes de las bases registrados en los documentos relacionados
> donde el impuesto del documento relacionado sea igual al campo
> ImpuestoP de este elemento y la TasaOCuotaDR del documento
> relacionado sea igual al campo TasaOCuotaP de este elemento.
**Cause:**
* The SAT validator enforces a strict arithmetic relationship between three fields that are printed in the payment complement XML:
```
BaseP == round(BaseDR / EquivalenciaDR, 6)
```
* When `percentage_paid` is a non-terminating decimal (which happens for any partial MXN payment against a USD invoice), the internal `raw_base` float carries more precision than the 6-decimal-place `BaseDR` that is actually written to the XML:
```
percentage_paid = 1762.45 / 5220 = 0.33763409961685825...
raw_base = 4500 × 0.33763... = 1519.353448275862...
BaseDR (XML) = float_round(raw_base, 6) = 1519.353448
```
* The old code then used `raw_base` (the unrounded internal value) as the dividend when computing BaseP:
```
BaseP (old) = float_round(1519.353448275862... / 0.0587483333, 6)
= 25862.068980 ← written to <TrasladoP BaseP="...">
```
* The PAC performs the same division using only what it can read from the XML. The already-rounded BaseDR:
```
BaseP (PAC) = round(1519.353448 / 0.0587483333, 6)
= 25862.068975
```
* The 6th-decimal mismatch (25862.068980 ≠ 25862.068975) triggers CRP20268 and the document is rejected.
* The same issue occurs with a full MXN payment on a different date than the invoice when multiple invoice lines cause the rounded aggregate to diverge from the sum of per-line raw values.
**Fix:**
In `_l10n_mx_edi_add_payment_cfdi_values` (`account_move.py`), the block that builds the BaseP aggregation list was changed from iterating over raw per-`base_line` amounts to building a single synthetic entry per invoice using the already-rounded document-level `BaseDR` values (`tax_details['base']`) as the dividend:
- Before : wrong: raw_base ≠ BaseDR printed in the XML
'raw_base': tax_details['raw_base'] / inv_rate
- After : correct: 'base' == BaseDR, the exact value in the XML
'raw_base': tax_details['base'] / inv_rate
This guarantees that Odoo and the PAC divide the identical value, producing the identical 6-decimal result.
The `base_line_cfdi_values_mx_curr_list` path (used only for the `Totales` summary fields rounded to 2 dp) is left unchanged because the CRP20268 rule does not apply to that block.
opw-6468077
Forward-Port-Of: odoo/enterprise#128357Consolidated Malaysian e-invoice reports now group point-of-sale receipts only when they are truly consecutive on the same device. This prevents unrelated receipts or refund-only transactions from being hidden inside misleading ranges, making reports easier to verify and more accurate for compliance.
Original PR description
A consolidated invoice reports a range of PoS receipts per line, so every line claims that the receipts between its two endpoints follow each other. That claim was only loosely enforced: lines were…
A consolidated invoice reports a range of PoS receipts per line, so every line claims that the receipts between its two endpoints follow each other. That claim was only loosely enforced: lines were built by walking every order of the sessions involved and breaking whenever one was not part of the batch. Two receipts that were both in the batch were therefore always merged, whatever sat between them. This breaks down as soon as several devices sell under one config. Receipt numbers come from a counter local to each device, so two orders taken on two devices can carry adjacent numbers without being consecutive receipts, and were reported as a range covering receipts that were never part of it. Lines are now built from an explicit continuity test on both `sequence_number`, gapless within a config, and the receipt reference read from `pos_reference`, which must name the same device and the next receipt number. Neither is sufficient alone: an order can reach the database later than its ticket was opened, so following sequence numbers do not imply consecutive receipts either. A receipt made exclusively of refund lines is no longer merged into the line of the sales it follows. Refunds are usually rung up shortly after the sale they cancel, which made them contiguous with it and hid both inside a netted total - two sales and the two refunds cancelling them were reported as a single line of 0.00. An order mixing refund lines with new sales remains a receipt of its own, reported like any other with the amounts it refunded already deducted from it. The orders linked to a consolidated invoice are also listed in the order in which they are reported on it, rather than in the reverse chronological one used by default for PoS orders, so that the two can be read together. [task-6166533](https://www.odoo.com/odoo/my-tasks/6166533)
Fixes a billing issue where manually increasing a timesheet invoice could prevent later time entries from being invoiced. Businesses can now bill future timesheet periods correctly while keeping refund-related safeguards intact.
Original PR description
## Issue When a use rmanually increases the invoiced quantity on a timesheet invoice, subsequent timesheets logged in future periods can no longer be invoiced. The system assumes that the most recent…
## Issue
When a use rmanually increases the invoiced quantity on a timesheet invoice, subsequent timesheets logged in future periods can no longer be invoiced. The system assumes that the most recent logged hours are already covered by the previously over-invoiced amount, blocking the billing of the most recent timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P
- Product Type: Service
- Invoicing Policy: Based on Timesheets
- Create on Order: (Project &) Task
3. Create an SO:
- Customer: Any
- Product: P (any quantity)
- Confirm
4. Record hours on the task created:
- 06/01/2026 (June 1st): 1 hour
- 07/01/2026 (July 1st): 1 hour
5. Create the invoice for the June timesheet entry (by setting a timesheets period when creating the invoice), then **change the quantity to any value strictly greater than 2** and confirm.
6. Create the invoice for July
7. **An "Invalid Operation" error appears, stating that there's nothing to invoice, even though the timesheet entry from July was never invoiced.**
## Cause
Since https://github.com/odoo/odoo/commit/3b86ac3d180993239b63fd305ee0ba1f17f7eb20, the computation of the quantity to invoice changed to include the difference between the quantity delivered and the quantity already invoiced. In the flow described by the steps to reproduce above, the quantity to invoice is larger than the quantity delivered, making the `qty_to_invoice` equal to `0.0`.
https://github.com/odoo/odoo/blob/626d31fa0191bfe7ca8e1fcf177357eeeb60f2c7/addons/sale_timesheet/models/sale_order.py#L331-L334
The reason for the fix above being the partial refunding of timesheet-related invoices, we can keep that solution when working with refunded invoices, and keep the previous behavior for other cases.
This solution solves the issue in the steps to reproduce above, but was also manually tested on the issues from https://github.com/odoo/odoo/commit/3b86ac3d180993239b63fd305ee0ba1f17f7eb20 and https://github.com/odoo/odoo/commit/64cf4afabd0c5ef040cf87fc6f3125fe0bb81bbb.
opw-6485674
Forward-Port-Of: odoo/odoo#286301
Forward-Port-Of: odoo/odoo#284470This fix ensures Adyen payment information is read from the correct part of the payment notification. It helps prevent payment records from being updated with wrong or missing values, improving transaction reliability for businesses using Adyen.
Original PR description
opw-6512723 Forward-Port-Of: odoo/odoo#286391 Forward-Port-Of: odoo/odoo#284773
Website theme installation now correctly applies the selected theme's layout changes, such as headers and footers. This prevents customers from seeing an incomplete or unchanged website design after using the website configurator.
Original PR description
--- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286407 Forward-Port-Of: odoo/odoo#283174
This fixes a mobile usability issue in the HTML editor where users could not drag and drop table cells inside Todo items. The change prevents the browser's touch handling from interrupting the drag action, making table editing work as expected on phones and tablets.
Original PR description
Steps to Reproduce - Insert a table inside a Todo item. - Long-press the table menu to open the drag-and-drop overlay. - Try to drag and drop table cells. Issue: - Table cells cannot be dragged and dropped on mobile devices. Cause: - On mobile devices, the browser fires `pointercancel`/`pointerleave` during a drag operation, which ends the drag operation prematurely. As a result, subsequent `pointermove` events are not triggered causing the drag-and-drop operation to fail. Solution: - Add `touch-action: none` to the table menu element. This prevents the browser default touch handling from interfering with the drag operation, allowing `pointermove` events to continue and drag-and-drop to work correctly on mobile devices. task-6201176 Forward-Port-Of: odoo/odoo#283866 Forward-Port-Of: odoo/odoo#267680
Odoo now records a VoIP call more accurately when it is answered or rejected in another phone application such as Linphone. This prevents calls from being incorrectly shown as missed and gives users a clearer call history when multiple VoIP tools are open.
Original PR description
Steps to reproduce: - Have an external VoIP software configured (e.g. Linphone) - Have your Odoo configured and opened too - Call your VoIP number, using your smartphone => Both the softphone and…
Steps to reproduce: - Have an external VoIP software configured (e.g. Linphone) - Have your Odoo configured and opened too - Call your VoIP number, using your smartphone => Both the softphone and Linphone ring - Answer or reject using Linphone => The VoIP call record in Odoo immediately switches from "Trying to call" to "Missed". While there was no guarantee for our VoIP integration to work alongside Linphone in 19.0, we decided this should be an easy safe enough fix. Starting 19.2 (with [1]), the fix will be simplified and hopefully prettier thanks to the ameliorations that were made. After this fix, provided Odoo is open while Linphone is used, the call records will now switch to the right terminated / rejected status, still immediately once Linphone answers / rejects. In future versions and especially 20.0+, the system will be different and will allow way more features like this one to work better (e.g. here the call record only even exists if Odoo is opened while using Linphone and we won't have any information about the call duration). [1]: https://github.com/odoo/enterprise/commit/942f32316ab02d8c739fe7fdd5ec2bdde472a68e task-6449259 Forward-Port-Of: odoo/enterprise#130047 Forward-Port-Of: odoo/enterprise#127077
Refund payslips are now recalculated correctly whenever worked days or payslip lines are recomputed, not only after a reset. This helps ensure payroll corrections remain accurate and reduces the risk of incorrect refund amounts.
Original PR description
Instead of only resetting correctly the refund payslip in the reset, ensure that anytime we recompute worked days or lines, the refund is correctly computed. Forward-Port-Of: odoo/enterprise#129332
Belgian payroll now correctly treats public holidays as unpaid when they fall after an employee's guaranteed sick pay period has ended, even if a new medical certificate starts that same day. This prevents accidental overpayment and improves payroll accuracy for long-term sickness cases.
Original PR description
Steps to reproduce: - Employee on full-time sickness certified month by month (one hr.leave per medical certificate), guaranteed salary period already exhausted. - Compute payroll for the month whose public holiday falls on the first day of a new certificate. - Public holiday work entry stays paid, every other day that month is correctly unpaid. Cause: the 30-day lookback compared exact datetimes instead of calendar dates, so a certificate starting the same day as the holiday but later in clock-time got excluded. Also ignored timezone: date_from is stored in UTC and needs localizing before taking .date(). Fix: compare local calendar dates via hr.leave's own request_date_from/request_date_to instead of raw UTC datetimes. Added a regression test for the split-certificate case. Task 6512860 Forward-Port-Of: odoo/enterprise#129889
Dark mode now uses the full expected color palette and avoids mixing light-mode colors into dark-mode styling. This prevents missing or incorrect colors in screens and labels that rely on automatic color selection.
Original PR description
1) The "$o-colors" css variable was rebuilt in "secondary_variables.dark.scss", which loads after community's "primary_variables.scss" already assigned it via !default. The dark "$o-colors: ()!default;" reset was silently skipped, so dark colors were appended onto the light ones instead of replacing them, doubling "$o-colors" to 24 entries. => Moving this logic to "primary_variables.dark.scss", which correctly loads first. 2) "$o-colors-secondary-original" only listed 18 colors instead of the 44 used in the original "$o-colors-secondary" of light mode, meaning the dark mode's total palette ($o-colors-complete) had far fewer entries than the 56 that the JS helper "getColor()" assumes leading to missing coloring. => Extending the list so that the dark mode "$o-colors-secondary-original" matches the original "$o-colors-secondary" light mode's 44 colors.
Analytic plan applicability now falls back to the default setting when a screen does not provide a business context, even if a company-specific rule exists for invoices. This prevents unrelated areas like Work Centers or Employee views from incorrectly requiring analytic distribution.
Original PR description
### Issue: When an analytic plan has applicability lines with a company filter, the applicability is incorrectly applied on views that do not define a `business_domain`, such as Work Centers or…
### Issue: When an analytic plan has applicability lines with a company filter, the applicability is incorrectly applied on views that do not define a `business_domain`, such as Work Centers or Employee views For example, if a plan has: - Default Applicability: Unavailable - A line with Domain: Invoice, Company: My Company, Applicability: Mandatory Opening the analytic distribution on a Work Center shows `Mandatory` instead of the default `Unavailable` ### Cause: In commit https://github.com/odoo/odoo/commit/ffcf2ee1a3185ef73db93bfd95625844506692c5 `_get_score` was updated to return `0.5` when the applicability line's company matches the caller's company, even when no `business_domain` is provided In `_get_applicability`, the loop selects the first rule whose score exceeds the current minimum, which starts at `0`: https://github.com/odoo/odoo/blob/710e056e5171af2ab72d7d7793da3518f12faf5e/addons/analytic/models/analytic_plan.py#L255-L264 A score of `0.5` is enough to win over the default applicability, so a company-only match on a domain-specific rule incorrectly overrides the default when no `business_domain` is passed ### Steps to reproduce: - Install `mrp` and `accountant` with demo data - Enable Analytic Accounting in Settings - Open the Internal analytic plan and edit its applicability line: -- Remove the account prefix -- Default Applicability: Unavailable -- Domain: Invoice, Company: My Company (SF), Applicability: Mandatory - Go to Manufacturing > Configuration > Work Centers - Open any work center and click on Analytic Distribution Before the fix, Internal is shown as Mandatory instead of Unavailable Removing the company from the applicability line confirms the issue opw-6404884 Forward-Port-Of: odoo/odoo#280312
Accountants without company access rights can now generate BOE files for Spanish Modelo 115 tax reports without receiving an access error. The change prevents the export wizard from attempting an unnecessary company update, keeping the workflow available to accounting users as intended.
Original PR description
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax…
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax reports, even though exporting a BOE file has nothing to do with editing company configuration and should be available to any accountant. Steps to reproduce: ------------------- * Log in as a user with accounting rights only (not part of the Companies > Access Rights group) * Go to Accounting > Reporting > Tax Return, open the Mod 115 report * Click the gear icon > BOE > Generate BOE > Observation: An AccessError is raised: "You are not allowed to modify 'Companies' (res.company) ... This operation is allowed for the following groups: Access Rights", even though the user is not trying to edit the company. Why the fix: ------------ The BOE wizard shared by Mod 111/115/303 declares a `company_partner_id` field, related to `company_id.partner_id`, with `readonly=False`. That field is invisible in every view and only exists to compute the domain of `partner_bank_id`; it was never meant to be edited by the user. Because it is declared writable, the ORM attaches an inverse to the related field, so saving the wizard (which happens when generating the BOE, since the field, though invisible, is still part of the view and thus of the saved values) writes `company.partner_id` back onto `res.company`, even though the value never actually changes. That implicit write requires write access on res.company, which is only granted to the Access Rights group, causing the error for regular accountants. Dropping `readonly=False` keeps the field as a plain readonly related field, still usable for the bank account domain, without ever triggering that spurious write. opw-6388869 Forward-Port-Of: odoo/enterprise#130168 Forward-Port-Of: odoo/enterprise#126661
This fixes an issue where certain changed and recombined manufacturing orders could incorrectly produce double the intended quantity or fail during valuation. Manufacturing validation now correctly splits quantities across related finished product lines and values them together, helping avoid inventory and costing errors.
Original PR description
When the quantity of a make-to-order manufacturing order is changed, its finished move is copied to propagate the extra quantity downstream. In some cases (after splitting then merging back…
When the quantity of a make-to-order manufacturing order is changed, its finished move is copied to propagate the extra quantity downstream. In some cases (after splitting then merging back productions) that copy is not merged back, so the order ends up with two finished moves for the same product. On validation, two things then go wrong: - _post_inventory writes the produced quantity on each finished move, so both get the full quantity and the production is doubled - mrp_account._cal_price prices the finished move and calls ensure_one(), which raises "Expected singleton" for an average/fifo product, so "Produce All" fails Split the produced quantity across the finished moves with unit_factor (like _set_qty_producing already does), and price them as a whole instead of expecting a single finished move. Steps to reproduce: - Create a BoM for product A with the MTO route, and a component B - Create a Sale Order for 30 units of A, confirm it - Split the MO into 3 productions of 10, merge two of them - On the third, Update Quantity 10 -> 15, then Produce All - The product should be produced once (15, not 30), with A valued in average/fifo it instead of an error. opw-6242504 opw-6310972 opw-6307025 opw-6354739 Forward-Port-Of: odoo/odoo#286173 Forward-Port-Of: odoo/odoo#269254
This fixes the import of Factur-X e-invoices received through Peppol by correctly reading the embedded XML inside the PDF. It prevents missing or empty invoice records and improves detection of self-billed documents, helping French e-invoicing flows process documents reliably.
Original PR description
When importing new documents from Peppol into the database, we determine whether they are self-billed by checking a Type Code in the XML file. Factur-X is an hybrid format where the XML is embedded inside a PDF. Currently, we are not extracting the XML before searching for that Type Code, and it leads to an error that prevents the document from being imported correctly: - V17, V18: An empty invoice is created and linked with the attachment. - V19+: Only the attachment is created. Additionnaly, we only check for InvoiceTypeCode or CreditNoteTypeCode, but the CII XML format embedded inside the hybrid Factur-X format use TypeCode instead. This PR aims at fixing both these issues. Ticket: opw-6417682 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285552 Forward-Port-Of: odoo/odoo#280714
This fixes an issue where signatures could disappear from downloaded signed PDFs when the original file had unusual page positioning. Signed documents now place fields within the visible page area, so users can trust that completed PDFs match the preview.
Original PR description
Steps to reproduce (version 16+): 1) Obtain a pdf with a negative origin point: This can occur when a customer exports a pdf from another software, or it can be made manually using a python script 2) In the sign app, upload the pdf and create a new template, add a signature field to the document. 3) Sign the document. The preview will load correctly and the signature will be visible 4) Download and open the signed pdf. The signature is not on the document Notes: Issue occurs because the signature was added to the pdf outside of the visible area. The preview works because the signature is rendered on top of the unsigned document in the correct location. The issue can be fixed applying a translation to the canvas. Ticket: [6317223](https://www.odoo.com/odoo/project/49/tasks/6317223?debug=assets) Forward-Port-Of: odoo/enterprise#127711 Forward-Port-Of: odoo/enterprise#121960
Appraisals now keep the generic template chosen by the user when they are confirmed or reset. This prevents records from being silently reassigned to a different template, so reporting and filtering by appraisal template remain accurate.
Original PR description
Issue: When a user selects a generic appraisal template other than the first one, confirming or resetting the appraisal silently replaces that selection with the first generic template. Filtering…
Issue: When a user selects a generic appraisal template other than the first one, confirming or resetting the appraisal silently replaces that selection with the first generic template. Filtering appraisals by the originally selected template then fails to return the appraisal. Steps to reproduce: * Configure multiple appraisal templates without department restrictions. * Create an appraisal and select a template other than the first one. * Confirm the appraisal. * Filter appraisals by the selected template. Cause: `_compute_appraisal_template()` only preserved templates directly linked to the appraisal's department. Generic templates have no department relation, so a valid selected template was discarded whenever the computation was triggered by a state dependent department recomputation. The generic fallback then stored the first template instead. https://github.com/odoo/enterprise/blob/5c57ccbb13269af28de0a6c7f35454be52424f43/hr_appraisal/models/hr_appraisal.py#L195-L209 Solution: We need to distinguish the generic default loaded on an unsaved form from a compatible generic template already stored on an appraisal. Preserve the latter across recomputations while retaining department template priority during creation and rejecting templates that no longer match the appraisal's department or company. opw-6449041 Forward-Port-Of: odoo/enterprise#129635 Forward-Port-Of: odoo/enterprise#127566
This fixes a navigation issue where opening Helpdesk tickets from an email alias could crash because the system reused the wrong background context. Users can now move from an alias to its related Helpdesk team and view tickets normally, improving reliability for support workflows.
Original PR description
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe…
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe error ### Description of the issue/feature this PR addresses: **Issue:** When navigating from an email alias to its parent document (e.g., a Helpdesk Team), the web client incorrectly retains the `active_id` and `active_model` of the alias in the context. **Solution:** We updated the `action_view_ticket` method in `helpdesk.team` to explicitly inject the correct `active_model` and `active_id` into the context before calling `_for_xml_id`. This overwrites the polluted Alias data before the window action is evaluated. ### Current behavior before PR: Clicking the "Tickets" smart button passes the old alias context into the action. This bad data flows into the ticket view, which attempts to look up a Helpdesk Team using the Alias's ID to generate the empty list help message, resulting in a `MissingError`. ### Desired behavior after PR: The Python action sanitizes the context at the source, ensuring that the XML action and subsequent view evaluations receive the correct Helpdesk Team ID. Ultimately, the view will load normally without crashing. opw-6395638 Forward-Port-Of: odoo/enterprise#130088 Forward-Port-Of: odoo/enterprise#125232
The Helpdesk SLA Status Analysis report now measures Hours Open from ticket creation until closure, matching the main Ticket Analysis report. This prevents tickets that were assigned immediately from showing no open time and gives managers a more accurate view of service performance.
Original PR description
1. Open Helpdesk > Tickets and create a ticket on the team "Customer Care", assigned to yourself 2. More than an hour later, move it to the "Solved" stage to close it 3. Open Helpdesk > Reporting > Ticket Analysis, switch to the pivot view and pick the "Hours Open" measure -> the ticket holds the hour it stayed open 4. Open Helpdesk > Reporting > SLA Status Analysis and pick the "Hours Open" measure as well -> the ticket holds nothing, as it was assigned as soon as it was created odoo/enterprise#47454 added the "Hours Open" measure of the ticket analysis to the SLA status analysis, but computes it up to the assignment date instead of the closing date. The measure therefore holds the hours until the ticket was assigned, which the report already offers as "Working Hours to Assign". With this commit, both reports count the hours from the creation of the ticket to its closing. Forward-Port-Of: odoo/enterprise#130263 Forward-Port-Of: odoo/enterprise#130170
Fixed an issue in the HTML editor where pressing Enter in a list item containing a table did not split the bullet as expected. This makes editing mixed content in bullet lists more predictable and prevents accidentally moving larger list content out of the list.
Original PR description
### Steps to reproduce: - Insert a bullet list - Inside of the list, insert a table - Write before and/or after the table (in the same list item) - Press enter before and/or after the inserted text - Notice that the bullet is not split like in a normal list ### Root Cause: - On Enter, list plugin checked whether the list item contained unsplittable element. Since the table was inside the list item, it always treated the list item as unsplittable, even when the cursor was outside the table. As a result, the list item could never be split. ### Solution: - Instead of checking the whole list item, walk up from the split target to the list item and look for an unsplittable element along the way. This allows the list item to split normally when the cursor is outside the unsplittable. task-6449843 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286028 Forward-Port-Of: odoo/odoo#280903
This fix prevents already-paid online orders from being reopened as abandoned carts during a short timing window after redirect payments. It helps avoid incorrect order totals and false payment mismatch warnings on the confirmation page, improving checkout reliability for customers using alternate pricelists or currencies.
Original PR description
**Steps to reproduce:** - Install the `website_sale` module. - Configure the Mollie payment provider and publish it. - Enable the PLN currency. - Create a PLN pricelist and make it selectable on the…
**Steps to reproduce:** - Install the `website_sale` module. - Configure the Mollie payment provider and publish it. - Enable the PLN currency. - Create a PLN pricelist and make it selectable on the website. - Open the Mitchell Admin customer record and, under the `Sales & Purchase` tab, assign the USD pricelist. - Go to the website shop, switch the pricelist to PLN, add a product to the cart, and proceed to checkout. - Pay through Mollie and mark the transaction as paid in Mollie. **Issue:** - After a successful redirect payment, the confirmation page displays an amount-mismatch warning, even though the payment was accepted by the provider for the correct amount. **Root cause:** - Mollie's return request and webhook can arrive within milliseconds of each other. Both requests attempt to create a `payment_data` record referencing the same `payment_transaction`. - At the same time, the cron holds a `FOR UPDATE` lock on the transaction while processing the first payload. The `FOR KEY SHARE` lock automatically acquired by PostgreSQL during the foreign-key check of the `payment_data` insertion conflicts with this `FOR UPDATE` lock. This results in a serialization failure and rolls back the post-processing job that would have confirmed the sale order (`draft` → `sale`). - This temporarily leaves the transaction in `done` state while the sale order is still in `draft`. - `sale_reset()` then clears both the session cart key and the selected- pricelist key. When `/shop/confirmation` renders, `request.cart` is resolved through `_get_and_cache_current_cart()`. The abandoned-cart recovery branch finds the still-draft sale order and calls `_update_address()` on it. - Since the selected-pricelist key is no longer present in the session, the customer's default USD pricelist is applied. `_recompute_prices()` then changes the sale order total. However, the payment transaction still contains the original PLN amount, so `_get_status_message()` detects the difference and displays the amount-mismatch warning. **Solution:** - Before calling `_update_address()` and `_verify_cart()` on an abandoned-cart candidate, check the state of its latest transaction. - If the transaction is `pending`, `authorized`, or `done`, discard the candidate and return an empty cart. - This matches the guard already present in the session-cart branch of the same method, keeping both paths consistent. - The serialization failure itself is expected and handled by Odoo's retry mechanism. This fix prevents the resulting side-effect window from allowing an already-paid order to be recovered as an abandoned cart and repriced. opw-6470325 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Italian invoice generation no longer fails when a company does not use email or SDI delivery checks. The attachment selection option is also shown more reliably for Italian companies, helping users complete invoice sending without interruptions.
Original PR description
When generating an invoice with an Italian company not checking mail nor SDI, a traceback is raised This commit also update the condition to show attachment selector on `account.move.send` for Italian companies Task [link](https://www.odoo.com/odoo/project.task/6514516) task-6514516
Translated table of contents navigation now keeps plain, consistent link text even when translated headings are styled. Social and sharing snippets also reuse the same styling rules in translation mode, reducing display inconsistencies and maintenance risk.
Original PR description
### [REF] website, *: prevent style in navbar of translated table of content *: html_builder The snippet "Table of Content" copies the content of the title in its navbar, taking care of removing…
### [REF] website, *: prevent style in navbar of translated table of content *: html_builder The snippet "Table of Content" copies the content of the title in its navbar, taking care of removing style to have plain text in the navbar. In translate, to try to achieve this as well, it uses some special logic with `o_translation_without_style`, a second translation hash,... But, if one of the title has no style in the original language, but a translation adds a style, then the term with style is used in the navbar. This commit uses `o_translate_inline` on the links of the navbar. With this, they make together a single translation term which is always distinct from the terms of the titles. The copy of the translated titles is made with the same plugin as in normal mode (with some adaptation to take into account the translation spans). Steps to reproduce: - Open website builder - Drop the "Table of Content" snippet - Save - Open website builder in translate mode - Select text in a title of the "Table of Content" snippet - Change its style: make it bold - Save - Bug: the term in the navbar is bold as well task-6304267 ### [REF] website: define style for translated social snippet in main css The snippets "Social Media" and "Share" needs additional rules to appear correctly in translate mode (if they have `o_translate_inline` on their links). Those were defined in a separate file for inside the builder. This is error prone because the content of those rules needs to be kept in sync with the general rules. To avoid the duplication, this commit moves those additional rules with the normal ones, so they share their content. task-6304267
This fix corrects how employee occupations are calculated for Belgian holiday attestations. It helps ensure payroll and departure documents reflect accurate occupation information, reducing the risk of incorrect HR payroll records.
Original PR description
This commit fixes the occupation computations which were wrong. Forward-Port-Of: odoo/enterprise#129837
Swiss payroll payment reports no longer fail when an employee is paid through a Revolut bank account. This keeps ISO20022 payment file generation working after recent bank account data model changes.
Original PR description
res.bank and res.partner.bank.bank_id were removed in saas-19.2: the bank's identity now lives directly on the bank account as bank_bic and bank_name. The Revolut detection added in fba51a6a77a still read bank_account.bank_id, raising an AttributeError when generating the ISO20022 payment report for Swiss payslips. Forward-Port-Of: odoo/enterprise#129915 Forward-Port-Of: odoo/enterprise#129156
This fixes an issue where adding delivery charges could cause Odoo to recalculate the unit of measure and then calculate shipping-related sales line amounts incorrectly. The change keeps the intended unit of measure when the delivery line is created, helping prevent wrong prices or totals on sales orders.
Original PR description
In this PR, https://github.com/odoo/odoo/pull/186250, the `product_uom` field was renamed to `product_uom_id`. However, in the `delivery` module, `product_uom_id` is dropped from the values when the…
In this PR, https://github.com/odoo/odoo/pull/186250, the `product_uom` field was renamed to `product_uom_id`. However, in the `delivery` module, `product_uom_id` is dropped from the values when the delivery line is created. This causes `product_uom_id` to be recomputed. This commit reintroduces `product_uom_id` in the values to prevent the field from being recomputed. **Description of the issue/feature this PR addresses:** For a strange reason, when a module inherits from `sale.order.line` and adds some computed fields with `precompute=True`. `price_unit`, `price_subtotal`, and `price_total` are computed incorrectly. I have attached a module to demonstrate the issue. https://github.com/user-attachments/assets/ebdd8695-c9d8-477b-b5cf-ba6d8d41e84a Without this change, the test fails, and Odoo incorrectly recomputes the fields, as shown in the video. <img width="1232" height="515" alt="image" src="https://github.com/user-attachments/assets/27370f1f-e7b7-4102-a606-0181b9d1a97a" /> When the ORM computes fields marked as `precompute=True`, in this function `_add_precomputed_values` https://github.com/odoo/odoo/blob/0d44f26d9b0fb1c1a5db463cf1f8dd0d3c72ba26/odoo/orm/models.py#L4836, `price_unit` is 0, but the records get `price_unit` from the product. Therefore, when [_compute_amount](https://github.com/odoo/odoo/blob/0d44f26d9b0fb1c1a5db463cf1f8dd0d3c72ba26/addons/sale/models/sale_order_line.py#L855) is called, the values are computed with an incorrect `price_unit`. <img width="1087" height="940" alt="image" src="https://github.com/user-attachments/assets/92feadf0-7f93-4acc-8f1a-931db0655fcc" /> **Steps to reproduce the issue:** - Install the attached module. [sale_precompute.zip](https://github.com/user-attachments/files/31265993/sale_precompute.zip) - Configure a delivery carrier as free for orders over 1, and set the fixed price to 5, for example. - Create a sales order and add a product with a value greater than 1. - Add the shipping method. The price should be 0. In the sales order line, `price_unit` is 0, but `price_subtotal` and `price_total` are equal to 5 (the product's sale price). For more context, this module is a simple example extracted from the OCA `product_secondary_unit` module, which adds a mixin with these fields: https://github.com/OCA/product-attribute/blob/18.0/product_secondary_unit/models/product_secondary_unit_mixin.py. In the `sale_order_secondary_unit` module, `sale.order.line` inherits from this mixin. You can see the error in this PR: https://github.com/OCA/sale-workflow/pull/4535. https://github.com/OCA/sale-workflow/actions/runs/32259842816/job/96090194021?pr=4535#step:8:509 I understand that this requires a deeper investigation into precompute to solve the underlying issue, but I propose setting `product_uom_id` in the `_prepare_delivery_line_vals` method as a temporary solution while the final solution is being investigated. I understand that this field should not have been removed from `_prepare_delivery_line_vals`; the referenced PR only renamed the field and did not intend to remove the value from this method. @Tecnativa @pedrobaeza @kcv-odoo @Feyensv coudl you please review this? --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284316 Forward-Port-Of: odoo/odoo#283551
The kitchen preparation display now correctly chooses an available point-of-sale configuration when it is shared across multiple setups. This prevents failures when no single configuration is selected and keeps shared kitchen displays working reliably, with minor visual adjustments included.
Original PR description
In this commit: ------------------- - We were passing the pos config id when loading the preparation display, but multiple configs can be configured, and no config is set when using all configs. Instead, the config is now resolved within the service from the loaded configs. task: 6512063 Related PR: https://github.com/odoo/odoo/pull/286169
Web Serial device handling has been moved out of Point of Sale into a shared module. This makes it easier to support serial-connected devices, such as scales, in other Odoo apps without duplicating work.
Original PR description
Enterprise PR: <https://github.com/odoo/enterprise/pull/129510> Before this commit, the code was interfacing with a Web Serial scale was included directly in the Point of Sale module. After this commit, the Web Serial code is extracted into a new module `iot_webserial`, which `point_of_sale` now depends on. This allows Web Serial devices to be used in other modules without duplicating the code. task-6485996 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr