Tuesday, September 8, 2026
74 changes · master
New functionality added to Odoo
Adds India-specific Schedule III Balance Sheet and Profit and Loss reports so companies can present financial information in the required statutory format. The reports use existing Schedule III account tags, helping businesses prepare compliant financial statements more easily.
Original PR description
Add the Schedule III Balance Sheet and Profit and Loss reports based on the account classification defined by the Schedule III Chart of Accounts. These reports use the Schedule III account tags present the financial information according to the required format. task-2381149
Delivery teams can now print CMR transport documents directly from the Barcode app. This works for individual transfers and batch deliveries, reducing the need to switch between apps during warehouse operations.
Original PR description
This commit creates a `bridge` module between `stock_fleet` and `stock_barcode` to allow users to `Print CMR` reports directly from the Barcode app for both picking and batch delivery operations. Community PR: odoo/odoo#280200 TaskId-6413050
Enhancements to existing features
Accrual entries now include clearer labels so users can better understand and track what was generated. The process also detects possible duplicate accruals for the same period, reducing the risk of repeated accounting entries and cleanup work.
Original PR description
This commit improves the creation of accrual entries by: 1. adding context dependant strings allowing the user to easily track what kind of accrual entry has been created. 2. adding a mechanism to detect and handle duplicate accrual entries for the same period. task-6484921
Resolved issues and error corrections
Peruvian electronic invoices now include cash rounding in the final amount due sent to SUNAT. This prevents mismatches between the displayed rounding and the payable total in generated XML documents, reducing invoice validation or compliance issues.
Original PR description
Steps to reproduce ------------------ 1. On a peruvian company, set a cash rounding method on an invoice 2. Post the invoice and generate the SUNAT UBL 2.1 XML -> the rounding is in…
Steps to reproduce ------------------ 1. On a peruvian company, set a cash rounding method on an invoice 2. Post the invoice and generate the SUNAT UBL 2.1 XML -> the rounding is in `PayableRoundingAmount` but `PayableAmount` still has the amount before rounding! Why it's happening ------------------ The generic code computes `PayableAmount` from `amount_residual`, and Peru overrides it to be the total tax included of the XML minus the prepaid amounts, because the residual can not be used there. Then odoo/odoo@b847552872ad changed the meaning of the totals. The cash rounding line is not part of the base lines anymore, its amount is kept aside in `cash_rounding_base_amount_currency` and the totals do not contain it anymore. The generic code stays correct because `amount_residual` already has the rounding inside but the Peru total using `tax_inclusive_amount_currency` is now the amount before rounding and this is what ends up in the `PayableAmount`! The fix ------- Add the cash rounding amount when computing the `PayableAmount`. opw-6509677 Forward-Port-Of: odoo/enterprise#130732 Forward-Port-Of: odoo/enterprise#129983
Code cleanup and technical improvements
Time off accruals are now calculated more consistently, especially around carryover dates, plan level changes, and accruals granted at the start of a period. This helps employees and HR teams see more accurate leave balances, including future balances shown in the dashboard.
Original PR description
## Issues ### Accrual allocation initialization issue For an accrual allocation, due to `_add_lastcall` initialization method, the first carryover date could be skipped. Indeed this method only…
## Issues
### Accrual allocation initialization issue
For an accrual allocation, due to `_add_lastcall` initialization method, the first carryover date could be skipped. Indeed this method only includes the following accrual "events" in his `nextcall` computation:
- Carryover days expiration date
- Current level next period
- Next level transition
PS: The job of _add_lastcalls was to guess the value of nextcall, lastcall and actual_lastcall after saving the allocation
### Accrual allocation `_process_accrual_plans` issue
For an accrual allocation, to compute the number of days when the "accrued_gain_time" is "start", the code is using a bool field (already_accrued) and was trying to use the same logic as when "accrued_gain_time" is "end", but adding another alternative at the end of the loop. This was a bad idea as it has been proven that it leads to complex, hard to maintain, long, and bug prone code.
Here is the (impossible) challenge of this code:
The function `_process_accrual_plans` tries to update the `number_of_days` according to the accrual "events" (carryover, carryover expiring days, level transition, level period transition-daily-monthly-...). But the problem is that sometimes, it has to process multiple accrual events to compute the `number_of_days` of the allocation in the far future (possible when the user wants to see his number_of_days in the future from the time off dashboard). In this case, when the "accrued_gain_time" is "start" and using 'already_accrued', here's how the function works :
- Run through each accrual event until the nextcall is past the target date (same as when "accrued_gain_time" is "end")
- Run the additionnal alternative
This alternative set 'already_accrued' to True so that the next accrual event in the main loop doesn't accrue the days to the allocation.
But this code also has to take care of each day cron run. In that case, here's how it behaves:
- If the allocation nextcall happens before the target date:
- Run one iteration of the main loop, which probably doesn't add the accrued days as already_accrued is probably True.
- Run the additionnal alternative
This means that all the logic has to be at the same time in the main accrual event loop, and in the alternative, which is really hard, and led to the spaghetti we have today (which still contains a lot of bugs).
To avoid this issue, this PR separates the logic of each case ("accrued_gain_time" "start" or "end"), and removes 'already_accrued'.
## Accrual allocation `_process_accrual_plans` new feature
To make it easier to use, this method now returns a dict containing the accrual values of the allocations on the `target_date`, and do not directly apply the changes on the allocation anymore. For instance, this avoid having to create 'fake_allocation' using `new(origin=self)`.
However, it adds a little bit of complexity from the code, as some function now have to pay attention whether they should read the value from the values in the dict representing the data of the allocation, of the fields themselves (see class description for more explanations).
## Behavior correction
Before this PR, a day belonged to a level only if it was between the start of this level (not included), and the start on the following level (included). As the start of the level was not included, this could lead to some small issues like the days are only accrued on the second days of the level, even when "accrued_gain_time" of the accrual plan is "start".
## Tests
As I spend a lot of time making the tests work, I took this opportunity to make a few changes, mostly to make test simpler to understand, or to correct the tests that were doing wrong assertions.
## Accrual allocation refactoring
- Deleting the property "already_accrued"
- Renaming
- lastcall -> last_accrual : as described in the definition of lastcall, this property contains the "Date of the last accrual allocation"
- actual_lastcall -> lastcall : simpler name
- postpone_max_days -> max_carriedover_duration: clearer name, moreover, the unit of time of this field can be either 'Days' or 'Hours'
- expiring_carryover_days -> previous_carryover_number_of_days: the field expiring_carryover_days contains the number of days the allocation had on the last carryover, so this names fits best
- Adding a few fields to the Form: when creating an allocation with an accrual plan in a Form, everything was running smoothly, and the number_of_days was computed correctly. However, when saving the allocation, as the lastcall, actual_lastcall and nextcall were not saved (no fields matching them in the Form), they needed to be recomputed. So the method _add_lastcall was called to try to approximate what were the values of each of these fields depending on the fields.Date.today().
- Getting out of the infinite compute dependency of `number_of_days`, `number_of_days_display` and `number_of_hours_display`. Those fields were 3 computes fields depending on each other (and the orm was not made to use it that way) -> `number_of_days` is not computed anymore, and the 2 other fields set it using 'inverse' (more standard way of doing things).
task-5977748
[upgrade-8081](https://github.com/odoo/upgrade/pull/8081)The barcode tools now use a reusable view that combines camera scanning and manual barcode entry. This makes the scanning experience easier to expand and lets teams control whether the manual input field automatically receives focus.
Original PR description
Input focus is made conditional for the BarcodeInput component, to allow users to disable it if desired. It is enabled by default task-4170451 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
On-hand quantity links in stock reports now open the detailed stock quantities view directly. Lot quantities also reuse the existing filtered view, making it easier for users to inspect stock details consistently.
Original PR description
This commit's changes: - Redirect the stock report's on-hand quantity to the quant action. - Make the lot quantity call the existing lot-filtered quant action. task-6445662 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
Website editors now have more control over product snippets, including grid or carousel layouts, column settings, and an option to show all products by website order. The settings have also been renamed and reorganized to make product presentation easier to configure.
Original PR description
**Post this commit:** - Added Orientation option for choosing Grid/Carousel layout. - Added an 'All' Filter to fetch products based on website sequence, rather than a specific rule. - Added Columns and Mobile columns to get more precision in terms of layout. - Renamed 'Show Variants' and 'Product Names'. - Reordered some of the settings. - Removed scrolling mode and making it single by default just for the product snippet. **Affected Version**-master Task-5167730 Docs PR: https://github.com/odoo/documentation/pull/16668
The command-line module tools now show which apps would be installed, upgraded, or removed before changes are applied. Users can also list installed apps and see pending updates, making database maintenance safer and easier to plan.
Original PR description
Show the modules that would be installed, upgraded or removed by running the command. Also allow listing installed modules. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Private calendar events are now counted when showing availability for appointments, while still hiding their details from people who should not see them. This helps prevent double-booking by showing users as busy even when the underlying event is private.
Original PR description
https://github.com/odoo/enterprise/pull/122675 When a user creates a private event, it shows up as 'Busy' with no additional details in the calendar module. This behavior was not mirrored in the…
https://github.com/odoo/enterprise/pull/122675 When a user creates a private event, it shows up as 'Busy' with no additional details in the calendar module. This behavior was not mirrored in the gantt view, meaning that we couldn't see the actual availability of the user. Additionally, if the user has their default calendar privacy set to private, any event created through the appointment view will automatically be private and hidden from all other (non-attending) users. Why? When we fetch events in calendar, we do a search and go through `_fetch_query` which censors non-public fields if the user doesn't have access to a private event. In the gantt view, however, we first do a `_read_group`. Since _read_group only returns ids and does not fetch fields directly, it filtered out the private events entirely. This is not entirely necessary, as whenever we want to read the events based on the retrieved ids, we still go through `_fetch_query` which censors them. Hiding the ids themselves is not needed, as they can be read using a search anyway. This commit therefore removed the `_read_group` event override. To make sure that the events are accounted for in the availability computation, we need to fetch them with sudo in `_get_busy_calendar_events` to bypass the censorship, as we need the partner_ids to create the interval mapping. The enterprise counterpart handles displaying the busy events in the gantt view. Task-6237805
Adds a new accounting setup for Indian companies that need to prepare financial statements under Schedule III. The existing Indian template is renamed for non-corporate entities, while corporate entities get the required account classifications and tags for compliant balance sheet and profit and loss reporting.
Original PR description
Schedule III provides the format and classification for the financial statements of companies required to prepare their financial statements accordingly. In this commit, rename the existing chart template to `Non-Corporate Entities` and add a new `Corporate Entities` chart template with the Chart of Accounts and account tags required for Schedule III. The account codes from the existing Chart of Accounts are also removed, as they are not prescribed by the Indian government and are not mandatory for any specific purpose. These will provide the required account classification and will be used by the Schedule III Balance Sheet and Profit and Loss reports. task-2381149
Belgian payroll now includes the monthly employer ONSS reduction directly on payslips, instead of only calculating it in the quarterly DMFA report. This gives companies earlier visibility into expected social security costs while keeping quarterly totals aligned with official reporting.
Original PR description
Currently the ONSS deductions for the employer are only calculated in the DMFA report every quarter, but are not reported on the payslips, so companies do not have a clear idea of the ONSS contribution they will have to pay. This commit adds a salary rule to calculate the monthly reduction amount using the computation in the DMFA report. Since the DMFA contribution and deduction are quaterly calculations, the payslips for the whole quarter are required for accurate calculations. Since the DMFA report requires validated payslips to access line IDs, the remuneration processing was adapted to use the localdict for the current payslip. The ONSS reduction rule is processed towards the end of the computation so the required lines should be available. task-6108623
When an employee leaves, Belgian payroll now automatically prepares the required departure documents at the right point in the payroll process. HR teams also get easier access to these documents from the employee profile, reducing missed paperwork and manual follow-up.
Original PR description
Context: Terminating an employee doesn't generate any document anymore. Termination payslip and holiday attest used to be generated in the end of collaboration wizard, but this was removed. This commit generate the termination documents (termination payslip, holiday attests, 13th month payslip, employment certificate): - As soon as the last monthly payslip for an employee is created within a payrun - Or when the last orphan payslip is validated for that employee A flag on the departure prevents regenerating them a second time when that payslip is later validated. It also add a dropdown to the employee profile when a departure is registered, regrouping all the termination documents that can be generated for that employee. The employment certificate can also now be generated anytime under the cog menu, before it was only possible to do it if the employee was on end of collaboration. task-6482307
Belgian payroll now supports tax and social contribution reductions for first hires in DMFA reporting. This helps businesses apply the correct employment incentives more reliably and avoid reporting issues, including a safeguard against crashes when configuration values are missing.
Original PR description
Implement reductions for first hires in belgium DMFA. Task-5476927
Users now receive a clear warning when trying to delete a worksheet template that is already used by worksheets. This helps prevent accidental deletion of related worksheets and reduces the risk of unintended data loss.
Original PR description
Deleting a worksheet template deletes all worksheets using it, which was not explicitly mentioned prior to this commit. Hence, we display a warning when the user wants to delete a worksheet template, if it is used anywhere preventing them to delete unintentionally all their worksheets. task-6417105
The accounting review menu has been reorganized so users can find asset, loan, purchase, and sales review items in more appropriate places. The change also improves how accrued order review workflows connect with the updated wizard, supporting a smoother finance review process.
Original PR description
This commit cleans up the review menu by moving items to their appropriate space, it also sets up the logic to properly interact with the improved `account.accrued.orders.wizard`. task-6484921
VoIP users can now choose separate audio outputs for incoming call ringtones and active call audio, making headset and speaker setups easier to manage. The in-call menu also keeps key settings available during calls, including device selection, Do Not Disturb, and access to phone numbers.
Original PR description
task-6533808
Frontdesk visitor records now allow only one host, matching the kiosk experience where visitors can select a single person to meet. This keeps front desk, SMS, and WhatsApp workflows consistent and reduces confusion from assigning multiple hosts in the back office.
Original PR description
Given that from the kiosk front end a visitor could select only 1 host at a time, It doesnt make sense to allow relating multiple hosts from the back end. This commit changes the `host_ids` field on `frontdesk.visitor` from a many2many field to a many2one field `host_id`. task-6514675
Odoo’s AI capabilities are now delivered through the Odoo AI in-app purchase service, with model selection managed centrally by Odoo. This makes AI features easier to maintain, allows model upgrades without user-side changes, and supports switching models during an AI session.
Original PR description
The AI features of Odoo are now provided through a new "Odoo AI" IAP service. - Provider/model selection is now handled on the IAP server side. The LLM model used will depend on the feature for which it is used. It can now be updated more easily when new models are released or older models are deprecated. - A generic format for messages has been introduced. Since we don't know in advance which LLM model will be used, it is necessary to store them in a provider agnostic format (see `ai/utils/types.py` for a description). Thanks to this, it is now possible to switch models during a session. task-[6334145](https://www.odoo.com/odoo/project/19060/tasks/6334145)
VoIP call forms and completed call activity messages now present key information more clearly, including summaries, status, date, duration, transcripts, and feedback. This helps users review calls faster while keeping sensitive cost information limited to administrators.
Original PR description
see commit messages Task-6512169
The invoice batch sending wizard now opens much faster, reducing wait times for large batches from minutes to seconds. Users can also review sent invoice details more easily, and the electronic invoicing banner now appears only when relevant.
Original PR description
[IMP] account: Enhance Send Batch Wizard - Previously, the Send Batch Wizard could take too long to appear due to doing too much unnecessary work. Benchmark results for 4,000 moves improved from 2 minutes to 7 seconds. - Added a dropdown menu to view the details of the sent invoices. - Fixed "Partner requested electronic invoices reception" banner was incorrectly displayed for non-default PEPPOL countries. task-6275573
Improves performance when opening point-of-sale registers for newly created companies in databases with very large accounting histories. The system now checks smaller company data first, avoiding slow scans of millions of accounting lines and making the experience much faster for users.
Original PR description
When you create a new company in a database that has a lot of existing account move lines and you attempt to open a PoS register from the list view, `_compute_company_has_template` checks…
When you create a new company in a database that has a lot of existing
account move lines and you attempt to open a PoS register from the list
view, `_compute_company_has_template` checks `_existing_accounting` for
the new company and wil run a sequential scan on the entire account_move_line
table followed by a nested loop as the query planner assumes AMLs company_ids
will be roughly evenly distributed.
This is not the case in a new company that has no/very few AMLs.
This is because the query ran is:
`SELECT COUNT(*) FROM
(SELECT FROM "account_move_line"
WHERE (
"account_move_line"."company_id" IN
(SELECT "res_company"."id" FROM
"res_company" WHERE
("res_company"."parent_path" LIKE '3/%')
))
LIMIT 1)`
and the values of company_id being searched for aren't known until the
subquery runs.
Running a query more like
`SELECT COUNT(*) FROM
account_move_line
WHERE company_id IN (%s)`
is much faster
Since res_company will always be a smaller table, we can do the inexpensive
search first and then pass in the values so Postgres can do a cheaper
search and return faster.
Benchmark time of `_existing_accounting`:
| Company 1 AML count | Company 2 AML count | Pre-fix | Post-fix | Multiplier |
|---|---|---|---|---|
| 10,000,000 | 0 | 700 Milliseconds | 500 Microseconds | 1,400x |
| 20,000,000 | 0 | 1.35 Seconds | 1 Millisecond | 1,350x |
| 20,000,000 | 20,000,000 | 2 Milliseconds |1.3 Milliseconds | 1.5x |
| 50,000,000 | 0 | 3.25 Seconds | 1 Millisecond | 3,250x |
| 100,000,000 | 0 | 5.3 Seconds | 1.3 Milliseconds | 4,075x |
Query Plan Before:
```
"Aggregate (cost=0.08..0.09 rows=1 width=8) (actual time=5573.001..5573.002 rows=1.00 loops=1)"
" Buffers: shared read=571435"
" -> Limit (cost=0.00..0.08 rows=1 width=0) (actual time=5572.995..5572.997 rows=0.00 loops=1)"
" Buffers: shared read=571435"
" -> Nested Loop (cost=0.00..https://github.com/odoo/odoo/commit/1571439c0c70c1f1dc3229421e696b97ce1678f8.33 rows=20000086 width=0) (actual time=5572.988..5572.989 rows=0.00 loops=1)"
" Join Filter: (account_move_line.company_id = res_company.id)"
" Buffers: shared read=571435"
" -> Seq Scan on account_move_line (cost=0.00..971432.72 rows=40000172 width=4) (actual time=0.432..1913.934 rows=40000000.00 loops=1)"
" Buffers: shared read=571431"
" -> Materialize (cost=0.00..4.03 rows=1 width=4) (actual time=0.000..0.000 rows=0.00 loops=40000000)"
" Storage: Memory Maximum Storage: 17kB"
" Buffers: shared read=4"
" -> Seq Scan on res_company (cost=0.00..4.03 rows=1 width=4) (actual time=0.785..0.785 rows=0.00 loops=1)"
" Filter: ((parent_path)::text ~~ '3/%'::text)"
" Rows Removed by Filter: 2"
" Buffers: shared read=4"
"Planning:"
" Buffers: shared hit=574 read=77"
"Planning Time: 15.323 ms"
"Execution Time: 5573.060 ms"
```
Query Plan After:
```
"Aggregate (cost=4.46..4.47 rows=1 width=8) (actual time=1.972..1.973 rows=1.00 loops=1)"
" Buffers: shared read=3"
" -> Limit (cost=0.44..4.46 rows=1 width=0) (actual time=1.967..1.968 rows=0.00 loops=1)"
" Buffers: shared read=3"
" -> Index Only Scan using account_move_line__company_id_index on account_move_line (cost=0.44..4.46 rows=1 width=0) (actual time=1.965..1.966 rows=0.00 loops=1)"
" Index Cond: (company_id = 3)"
" Heap Fetches: 0"
" Index Searches: 1"
" Buffers: shared read=3"
"Planning:"
" Buffers: shared hit=3"
"Planning Time: 0.219 ms"
"Execution Time: 1.998 ms"
```
opw-6513885
Forward-Port-Of: odoo/odoo#285096Appointment teams can now choose a specific email template to request customer feedback after a visit. This helps businesses collect service ratings in a controlled way, while leaving the setting empty by default to avoid unexpected emails for existing appointments.
Original PR description
Businesses often need to collect customer feedback after an appointment takes place to evaluate service quality and drive continuous improvement. Currently, Odoo only supports default templates for bookings and cancellations. This change links a specific mail template for post-visit feedback directly within the appointment type configuration. It is left empty by default to prevent breaking existing flows or sending unwanted emails during data migration. task-6268987
Belgian payroll now flags four additional issues before DMFA reporting, including mismatched location unit dates, incompatible remuneration codes, and payslips created before the company's first scheduled pay. These warnings help payroll teams catch compliance problems earlier and reduce the risk of incorrect social security declarations.
Original PR description
Adding 4 warnings on payroll dashboard and payslip model - Check if DMFA location unit cover the payslip date range - Check employers with code 317 don't have remuneration code 2 - Check that employers with 100% credit time don't have remuneration code 1 - Check that there is no payslips starting before company first scheduled pay Task: 6297320
Dutch SBR and ICP report submissions now go through Odoo's proxy service instead of connecting directly to Digipoort. This simplifies the submission process and lets businesses use either their own certificate or Odoo's shared group certificate.
Original PR description
*: l10n_nl_reports_sbr{,_icp,_status_info}
---
Description of the issue this commit addresses:
Dutch SBR and ICP reports still send directly to Digipoort with duplicated SOAP/signature code and no shared group-certificate flow.
---
Desired behavior after this commit is merged:
This commit routes both reports through IAP proxy signing/sending, and lets users submit with either a personal certificate or Odoo's group one.
---
IAP PR: https://github.com/odoo/iap-apps/pull/1593
Upgrade PR: https://github.com/odoo/upgrade/pull/11237
task-3439634
Forward-Port-Of: odoo/enterprise#117617Asset records now show the related bill lines more accurately when non-deductible taxes are involved. This prevents tax amounts from being hidden or incorrectly combined when several assets are created from the same vendor bill.
Original PR description
Previously, tax lines lacked a direct link to their parent base line, causing non-deductible taxes to be omitted from the asset's "Bills" tab and merged across multiple assets on the same bill. In this commit - `asset_base_line_id` is added to track the parent base line for asset generating accounts - overrides for `_prepare_base_line_tax_repartition_grouping_key` and `_prepare_tax_line_repartition_grouping_key` are added to force the tax engine to generate distinct tax lines per asset base line - `display_move_line_ids` is introduced to safely display both the base and tax lines in the "Bills" UI Task-6283724
Delivery operations can now include the Consignment Note (CMR) in their automatic print options when a delivery is validated. This also applies to point-of-sale deliveries, helping teams produce required transport paperwork with less manual effort.
Original PR description
Add the `Consignment Note (CMR)` report to the `Print on Validation` options for delivery-type operations, allowing users to automatically print the CMR report when validating a delivery. Apply the same behavior to POS deliveries. Enterprise PR: odoo/enterprise#128943 TaskId-6413050
The point-of-sale hamburger menu now opens as a dialog instead of a dropdown, making buttons larger and easier to tap. This improves day-to-day usability on touchscreen devices across standard POS, employee POS, restaurant, and self-order flows.
Original PR description
*pos_hr, pos_restaurant, pos_self_order In this PR we've changed the layout of the hamburger menu buttons so they're more easily clickable on devices. We've switched the dropdown menu with a dialog. task-6253998 Enterprise https://github.com/odoo/enterprise/pull/118961 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Bank statements can now be created in draft and explicitly posted when ready, giving accounting teams more control before finalization. The update also protects journals that require secure sequencing by preventing incompatible draft statements or journal type changes once hashed statements exist.
Original PR description
- Add `is_statement_posted` field: Introduced to support a "draft" state when creating a statement directly from the form view. - Add Post/Draft Actions: Added `action_post` and `action_draft` for statements. Resetting to draft (`action_draft`) will also trigger move hashing if required. task-5170039 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Italian electronic invoicing form now narrows the document type list based on the kind of accounting document being created. This makes it easier for users to choose the correct option quickly and reduces confusion during invoice entry.
Original PR description
In IT account move form view, add a filter on the document type field to display only possible document type for the move type. This should help users to find the right type faster and reduce confusion Task [link](https://www.odoo.com/odoo/project.task/6152691) task-6152691
Belgian payroll declarations are now tied directly to the payslips they report, making it easier to identify which payroll records need review when corrections are required. The reporting process also avoids reprocessing already reported payslips and uses a more reliable way to detect when corrections are needed.
Original PR description
- change the dmfa linking to be with its related payslips instead of employees which makes it easier in case of correction to track which payslips need checking - generalize dmfa correction to include web reports as well - only payslips that haven't been reported will be fetched instead of regenerating the report with all the month's payslips - the mechanism to detect the need of a correction is now more robust using a boolean that gets updated when a payslip is moved to done instead of the dates comparison previously used task-6326405
The point-of-sale hamburger menu buttons have been updated to match the newer modal layout. This creates a more consistent cashier experience across enterprise POS, IoT, and country-specific POS flows.
Original PR description
Following a change of layout for the hamburger menu buttons we've adapted these buttons to fit with the rest of the buttons in the modal. task-6253998 Community https://github.com/odoo/odoo/pull/267462
Payroll settings for Belgian localization now follow the company selected by the user. When switching companies from the payroll settings screen, users are redirected to the selected company’s settings list, reducing confusion and the risk of editing the wrong company’s configuration.
Original PR description
Currently, on Payroll config settings list and form view when we switch the company, We're still on the initially selected compan settings version. This behavior could lead to confusion and users have chance editing field that not belong to the company they switched. This PR expected If the user changes company while on the payroll setting version form or list view, they should be redirected to the newly selected company's setting version list view. How to access the view - Settings -> Payroll -> Configure (Belgian Localization section) task-6478413
This update refines the VoIP softphone experience with clearer icons, better button feedback, improved dropdown behavior, and more consistent spacing and alerts. These changes help users better understand call actions, settings, and activity context while reducing small visual issues that could cause confusion.
Original PR description
*: voip_hr_recruitment, voip_sale_subscription ### Update UI icons This PR updates several UI icons to improve consistency across actions and activity, while making them more relevant and easier to…
*: voip_hr_recruitment, voip_sale_subscription ### Update UI icons This PR updates several UI icons to improve consistency across actions and activity, while making them more relevant and easier to understand. ### Add active state to action buttons Prior to this PR, action buttons that opened dropdowns had no active state, which could lead to confusion as users might not know which button had opened the dropdown. This PR fixes the issue by adding a proper active state whenever a dropdown is open. ### Adapt top info message layout in softphone This PR revises the layout of the info message displayed at the top of the softphone to make it more consistent with other info alerts. ### Show correctly softphone navbar border Prior to this PR, the top border of the softphone navbar could disappear when scrolling. This PR ensures that the navbar border remains visible at all times. ### Review user settings dropdown items Prior to this PR, the "Do not disturb" dropdown items used custom utility classes that broke the hover state. This PR ensures that the dropdown correctly uses the standard dropdown item component. ### Add tooltip to many2one_reference_icon_field This PR adds a tooltip displaying the activity's "display_name" when hovering the `many2one_reference_icon_field` icon, helping users understand what the icon represents. ### Update o-voip-InCallView-pad spacing The current spacing in `o-voip-InCallView-pad` was initially designed to accommodate two additional action buttons that have not yet been implemented. For now, this creates an inconsistent spacing that can give the impression of a bug. This PR updates the spacing to better align with the action buttons currently available. task-5864994 --- 💡 _Preview screenshots are available in the task pad._ --- Requires: - https://github.com/odoo/odoo/pull/285621
Belgian payroll now includes certain unpaid but legally assimilated absences when calculating holiday pay tax provisions. When an employee leaves, the accrued yearly provision is cleared on the final payslip, improving payroll accuracy and preventing unreleased balances.
Original PR description
Before this commit, the holiday pay tax provision kept accruing every month but was never released, and it ignored the time off that is assimilated for the double holiday pay when it was not paid on the payslip. After this commit, the monthly provision base also covers assimilated but unpaid days (legal leave, public holidays, sickness, strike) so it keeps growing on those absences. On the employee last payslip the whole provision accrued during the year is emptied in a single negative line. taskid-6416109
Shop Floor users can now edit existing work instructions without starting from a blank editor, making small updates faster and less error-prone. The document viewer also works correctly for attached instructions, and newly captured pictures are added consistently to the instruction text without replacing worksheet documents.
Original PR description
[IMP] mrp_workorder: pre-fill the instruction when updating a step In the Shop Floor, "Update Instructions" opened the editor empty. A user making a small adjustment had to retype the whole…
[IMP] mrp_workorder: pre-fill the instruction when updating a step
In the Shop Floor, "Update Instructions" opened the editor empty.
A user making a small adjustment had to retype the whole instruction
again from scratch, without seeing what they were replacing.
The wizard is now pre-filled with the step's current instruction.
task-6459280
---
[FIX] mrp_workorder: fix the document viewer on Shop Floor
Opening the instructions of a step holding a document raised
"this.props.value.startsWith is not a function" and left the dialog empty.
Since https://github.com/odoo/odoo/commit/23a7d8cc5b1d2f38eaee35a4f6729fe128c1b62b , reading a
binary field over rpc no longer returns a base64 string but a dictionary
holding its content, name and size.
Adapt to read the content out of that dictionary.
---
[IMP] mrp_workorder{,_plm}: append new picture to the instruction
"Set a New Picture" wrote the picture to worksheet_document, the step's
document attachment, and stripped the inline images out of the note.
But that field is presented as a PDF worksheet everywhere else.
mrp_workorder_plm did the opposite on the ECO's BoM step: it added the
picture to the note and cleared the attachment. The same action led to
inconsistent results.
The picture is now appended at the end of the instruction, on the work
order step and on the ECO, and the worksheet_document is left untouched.
task-6459280Payroll-related fields on time types are now updated automatically when payroll data changes. This keeps absence and work-entry configurations consistent across multiple local payroll packages, reducing manual upkeep and payroll setup errors.
Original PR description
…roll data update Task: 6410286
Bank statement handling is improved so users can choose relevant reconciliation models directly during the reconciliation and posting flow. This makes cash and bank journal processing more guided, while ensuring required hashing and reconciliation steps happen at the right time.
Original PR description
[IMP] account_accountant: improve statement flow and reconciliation models This commit improves the bank statement lifecycle, reconciliation model selection, and journal hashing behavior: -Allow…
[IMP] account_accountant: improve statement flow and reconciliation models This commit improves the bank statement lifecycle, reconciliation model selection, and journal hashing behavior: -Allow selecting from the top 5 reconciliation models (prioritizing journal-specific models, then falling back to global ones) directly in the bank reconciliation widget. Selecting a model triggers it on creation. - Hashing: If a statement is created in a hashed journal, force the reconciliation of the remaining unreconciled entries and hash the moves. - Statements accessed from the base view now start in "draft" state (including their lines). Users can select a reconciliation model to apply upon posting. Hashing is also deferred to the posting step. - Adjust the `from_bank_reco` context key to ensure it is passed correctly, resolving an issue introduced by the removal of `onWillRender` task-5170039 [IMP] l10n_be_reports: Adding reco models for cash journals When creating a cash journal, we will first create new reco models. If the reco models already exist then we will just add the new journal to the match_journal_ids. task-5170039
Belgian payroll now requires companies to provide insurance details when they have employees and holiday pay fund details when they have workers. This helps ensure payslips and individual accounts include legally required information and warns businesses when required payroll company data is missing.
Original PR description
Purpose: Comply with Belgian regulation regarding holiday pay fund and insurance information that must be present on payslips and individual accounts. Implementation: - Insurance information is mandatory as soon as there are employees. Hence, enforced on the frontend. - Holiday pay fund is mandatory only when there are workers (see is_worker()). Hence, enforced on the frontend. - Modify existing warning for payroll company data to also track holiday pay fund & insurance fields. task: 6512348
Belgian payroll declarations now calculate research and development time more accurately across additional payslip types, including double holiday pay, 13th month, and termination payments. This helps ensure payroll reporting reflects the correct eligible R&D percentage for each employee period.
Original PR description
now we support more payslip structures in 274.XX ex: (DHP, 13Months, termination) we need to compute rd_percentage correctly this commit adds a method to compute the correct value, based on related employee versions. Task#6394245
Belgian payroll now rounds train travel distances down to the nearest available reimbursement bracket. This helps ensure reimbursements remain favorable to employees when exact travel brackets are not present in the train table.
Original PR description
In order to keep the train reimbursement in favor of the employees, all the train travel brackets have been floored to the nearest threshold available in the train table. Task: 6485650
VoIP administrators can now submit a request when no suitable phone numbers are immediately available. The system tracks these advanced orders over time and automatically creates or assigns the number once the provider fulfills the request, reducing manual follow-up and improving number availability.
Original PR description
```mermaid
sequenceDiagram
actor Admin
participant E as Enterprise VoIP
participant I as IAP Phone Service
participant T as Telnyx
Admin->>E: Search available numbers
E->>I: Search request
I->>T: Search inventory
T-->>Admin: No numbers
Admin->>E: Ask us / Submit Request
E->>I: Create advanced order + request UUID
I->>T: POST /advanced_orders
I-->>E: Accepted, no charge
Note over I,T: May remain pending for days
T-->>I: advanced_order.status_update = ordered
I->>T: Read generated standard Number Orders
I->>T: Fetch current monthly price
I->>I: Adopt standard orders + reserve billing
I->>T: Resolve/configure actual phone resources
I-->>E: Signed normal phone-number status events
E->>E: Create/reuse DID and assign requester
```
[Task-6529404](https://www.odoo.com/odoo/project.task/6529404)
IAP: https://github.com/odoo/iap-apps/pull/1842Self-order table sessions now pass the right order identifier through the preparation display flow. This helps keep QR-code-based table orders linked to the correct session, supporting a smoother restaurant ordering experience.
Original PR description
Add order_uuid parameters into pos_self_order_preparation_display controllers task-4934385 see https://github.com/odoo/odoo/pull/222152
Customers who open an order through a secure access link can now use the Return button without signing in. This fixes a broken self-service return flow and reduces friction for portal or guest customers returning delivered items.
Original PR description
Accessing an order on the website without connecting (only with the access token) doesn't allow the user to return the order (nothing happens when click on the "Return" button) Steps to reproduce: 1.…
Accessing an order on the website without connecting (only with the access token) doesn't allow the user to return the order (nothing happens when click on the "Return" button) Steps to reproduce: 1. Install Sales and Inventory 2. Go to Settings > Inventory > Operations and enable "Allow Spontaneous Returns" 3. Go to Sales and create a new quotation for customer "Acme Corporation" with product "Acoustic Bloc Screens" and confirm it 4. Go to the related delivery, set the quantity to 1 and validate it 5. Go back to the sale order and click the "Preview" smart button 6. Copy the url (/my/orders/<id>?access_token=...) and open it in an incognito window 7. Click the return button on the order page 8. Nothing happens Issue: The route to `my_order_return_data` requires the user to be logged in Solution: Make the routes `my_order_return_data` and `order_return_label` public For 19.4: access `return.reason` records with sudo (should be removed in master) For master: add read right on `return.reason` for all users opw-6487767 Forward-Port-Of: odoo/odoo#285290
Customers can now open signed report PDFs from the portal history without the preview crashing. This ensures Field Service reports remain accessible after signing while preserving the existing backend signing workflow.
Original PR description
Steps to reproduce: - Open a Field Service shift that has a customer (in progress or done). - Click Sign Report, sign, and confirm. - In History, click the report PDF. Before this commit: the preview crashed instead of opening. After this commit: the PDF opens on the portal. In the backend, the Sign button in the viewer still opens the template editor. The viewer Sign button needs the backend action manager, which does not exist on the portal. Requiring it blocked the preview itself. task-6535111
The Field Service planning Gantt view now opens without waiting for buffer time calculations to finish. This reduces potential delays for users, while the missing timing details are filled in automatically as soon as they are ready.
Original PR description
This commit fixes a potential performance issue in the Gantt view of Field Service when computing the buffer times. Prior to this commit, the view waited for the buffer times to be computed before rendering. With this commit, we trigger the buffer time computation in the background (i.e., fire and forget), while letting the view to render. As buffer times are computed, the view will be notified. no-task Forward-Port-Of: odoo/enterprise#130782
Fixes the French association balance sheet so active and passive totals include the right lines and avoid double-counting entries. This helps associations relying on Odoo financial reports see accurate balance sheet totals for compliance and decision-making.
Original PR description
## [FIX] l10n_fr_reports: fix missing lines in association balance sheet ### Issue: The Active part of the Balance Sheet for associations shows an incorrect `TOTAL ACTIVE` — some accounts are not…
## [FIX] l10n_fr_reports: fix missing lines in association balance sheet ### Issue: The Active part of the Balance Sheet for associations shows an incorrect `TOTAL ACTIVE` — some accounts are not propagated to their parent aggregations ### Cause: `ACTIF_IMMOBILISE` was missing `BIEN_PAR_DONATION` in its formula `ACTIF_CIRCULANT` was missing both `DISPONIBILITES` and `INSTRU_FINAN` in its formula ### Steps to reproduce: - Install `l10n_fr_reports` and `accountant` - Create a new FR asso company and switch to it - In Accounting Settings, select: France - Associations accounting plan - Create a Journal Entry: Account: 240000, Debit: 100 (BIEN_PAR_DONATION) Account: 512001, Debit: 100 (DISPONIBILITES) Account: 520000, Debit: 100 (INSTRU_FINAN) Account: 509000, Credit: 300 - Open the Balance Sheet and select Balance Sheet for associations Before the fix, `TOTAL ACTIVE` is 0 instead of 300 -------------- ## [FIX] l10n_fr_reports: add force_date_scope to asso cross_report ### Issue: The Passive part of the Balance Sheet for associations shows an incorrect `TOTAL PASSIVE` — the same move line is counted twice, once in `Retained earnings` and once in `Profit or loss for the year` ### Cause: In 19.1, `cross_report` aggregations were refactored: https://github.com/odoo/enterprise/commit/e699a14a1922d6b8a5bc6b88ad9c02a411e4b073 By default, a `cross_report` no longer forces its `date_scope` to the terms it calls — `force_date_scope` must now be explicitly passed in the subformula The association balance sheet was added in 19.1 without this parameter, so `RESULT_LEXERCICE` (`from_fiscalyear`) and `REPORT_NOUVEAU` (`to_beginning_of_fiscalyear`) both used the current report's `date_scope` instead of their own This caused both expressions to match the same entries and double the `TOTAL PASSIVE` ### Steps to reproduce: - Install `l10n_fr_reports` and `accountant` - Create a new FR asso company and switch to it - In Accounting Settings, select: France - Associations accounting plan - Create a Journal Entry: Account: 512001, Debit: 100 Account: 701100, Credit: 100 - Open the Balance Sheet and select Balance Sheet for associations Before the fix, `TOTAL PASSIVE` is 200 instead of 100 opw-6520639 Forward-Port-Of: odoo/enterprise#130147
This fix changes the registration flow so Odoo checks for an existing proxy user before contacting the external IAP service. It prevents rare concurrent registrations from leaving a database with outdated credentials that block later electronic document proxy calls.
Original PR description
In the current flow, _register_proxy_user first calls IAP create_user, then inserts the returned credentials in account_edi_proxy_client.user. At the same time, IAP create_user_2 may replace an existing user with a new one with different credentials before local persistence settles. So Tx A calls IAP and gets credentials for remote user U1. Tx B calls IAP and gets credentials for remote user U2 and unlinks U1. Tx A persists U1 locally. Tx B fails local insert due to a unique constraint. Client DB keeps U1 credentials, but IAP now expects U2. Subsequent proxy calls from the client fail. The DB is left with stale, unusable credentials and cannot recover without re-registration. IAP: https://github.com/odoo/iap-apps/pull/1816 task-6520501 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285648
The VoIP flow editor now keeps its visual layout consistent when used in right-to-left languages. This prevents misplaced connection points, resize controls, and overlapping text, making call flow editing more reliable for affected users.
Original PR description
The flow editor stores and computes its geometry using physical coordinates: inputs are placed on the left, outputs on the right, and nodes are resized from the bottom-right corner. RTLCSS mirrored some of the corresponding styles in RTL languages. This made ports and their labels inconsistent with SVG connections, changed the resize handle position and cursor, and altered the canvas transform origin. Prevent RTLCSS from flipping these geometry-dependent declarations so the editor behaves consistently in LTR and RTL. The record description in a terminal node's body was also missing min-width: 0 on its flex item, so text-truncate never actually constrained it: in RTL the text starts flush against the physically right-anchored output port labels, overlapping them.
Products added to an active Point of Sale session after it starts now receive the correct pricelist discounts or prices based on their category. This prevents unexpected full-price charges when staff add newly created products from categories covered by pricing rules.
Original PR description
Steps to reproduce: - Create a pricelist with a rule applied on a product category and make it available in the PoS - Open a PoS session - From the backend, create a product in a new category covered…
Steps to reproduce: - Create a pricelist with a rule applied on a product category and make it available in the PoS - Open a PoS session - From the backend, create a product in a new category covered by such a rule - Back in the PoS, find that product through Search > Search more and add it to the order Issue: The product is priced at its sale price, the pricelist rule set on its category is ignored. Cause: A product that is not part of the initial payload is loaded on the fly by load_product_from_pos, which sends back the rules returned by get_pos_ui_product_pricelist_item_by_product. That domain only matches the rules set on the template or on the variant, never the ones set on a product category, and the payload carries no product.category record either. The client therefore has neither the rule nor the category: parentCategories walks categ_id, which resolves to nothing, so getCategoryRulesIds returns no rule and getPrice falls back to the sale price. This stayed unnoticed because product.category is fully loaded when the session starts, along with every category rule, so only the categories created after the session was opened are missing. Fix: Send the categories of the loaded products, since a rule set on a parent category applies to its children - along with the products, and match the rules set on those categories in get_pos_ui_product_pricelist_item_by_product. The initial loading domain of product.pricelist.item no longer filters the category rules on the loaded categories: such a rule has to be loaded whatever the products sent to the client are, since a product of that category may be loaded later on. opw-6477745 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285721 Forward-Port-Of: odoo/odoo#284660
This fix stops Point of Sale invoice settlements in Argentina from incorrectly creating a separate zero-value invoice. It prevents settlement validation errors, allowing businesses to record customer payments reliably without consuming unnecessary electronic invoice numbers.
Original PR description
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices",…
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices", select that invoice, pay the balance by bank transfer and validate Issue: Validation fails with "There should be a single tax from the "VAT" tax group per line, but this is not the case for line ..." and the settlement cannot be recorded at all. The invoice being settled is correct; the rejected line belongs to a second invoice the PoS creates for the settlement itself. Cause: `setToInvoice` already refuses to invoice a settlement, but its condition, `is_settling_account and no line`, only describes a deposit: the deposit line is added at validation. When settling a due or an invoice, `is_settling_account` stays false and the order does carry lines - the settle lines - so the guard never fires. l10n_ar_pos then sets `to_invoice` on mount, as a sale must generate an electronic document in AR, and the settle line, untaxed on purpose since it pays an existing document rather than selling anything, reaches `_check_argentinean_invoice_taxes`. Fix: Refuse the flag as well when every line is a settle line. Such an order generates an empty document - all its lines, the receivable one included, have a zero balance - so it records nothing and only consumes a document number, which in AR means an AFIP number for a zero-amount invoice. An order that also sells something keeps its invoice, since the sale still has to be reported. Reconciliation is unaffected: it happens in `_reconcile_account_move_lines` at session close, and is already covered for both invoiced and non-invoiced settlement orders. opw-6464675 Forward-Port-Of: odoo/enterprise#129902 Forward-Port-Of: odoo/enterprise#128975
Odoo now ensures that a private chat between the same two people can only exist once, even when requests happen at the same time. This avoids duplicate conversations, preserves chat history when a contact is deleted, and keeps chat names clearer for users.
Original PR description
A chat between 2 persons should be unique, but it can currently be created multiple times because we don't have a proper constraint. A lock could be added in the main create route, but it wouldn't…
A chat between 2 persons should be unique, but it can currently be created multiple times because we don't have a proper constraint. A lock could be added in the main create route, but it wouldn't catch all flows, and it would also slow down the route and potentially create unnecessary retries. The best way to ensure uniqueness is to add a proper SQL constraint. As the combination of members depends on a x2many relational field, a new field is added to track this combination on the channel table. This is a similar pattern as combination_indices on product.product. The new field is computed during create and not with a compute: - compute are computed after creating the record in database, which makes it impossible to have SQL constraints to ensure the field is properly set, as it would initially not be set - chat with a partner that later gets deleted should be kept for history, in this case the member is deleted but the channel remains, re-computing the member_indices could lead to several channels with only the current user in it, which the constraint does not allow. This also significantly simplifies the search of existing chat channels. task-4664353 https://github.com/odoo/upgrade/pull/9120
This fix prevents electronic invoice imports from failing when an invoice line has a 100% discount together with tax included in the price. Odoo now recalculates the tax from the original price in this edge case, improving reliability for accounting document imports.
Original PR description
Due to the following commit: 01efd8cfcce3269ca6b88d549a670b08a90298cb, a division by zero error is raised when a 100% discount is used with a price-included tax. When the discount is 100%, it is impossible to retrieve the original tax amount before discount using a simple multiplication as the current raw_tax_amount_currency is zero. In that case, we need to recompute taxes using the original unit price before discount. opw-6242701 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283877 Forward-Port-Of: odoo/odoo#282430
Restored or duplicated databases using the neutralize option are now neutralized before the system makes them active for scheduled background work. This prevents automated actions from briefly running on a copied database before it has been safely disabled, reducing the risk of unintended emails, integrations, or jobs.
Original PR description
Before this commit: If you restore a backup or duplicate a database via /web/database/manager with "Neutralize" enabled, the cron workers had a small window between the creation of the Registry and the neutralization of the database where they could try to execute crons. Since neutralize_database does not require a full registry to run (it only does raw SQL operations), we can run it before the Registry creation (which has the effect, among other things, of making the cron workers aware of the new database). opw-6517819 Forward-Port-Of: odoo/odoo#286870 Forward-Port-Of: odoo/odoo#286532
The Bangladesh reports module no longer includes an outdated tax return setup that depended on a removed report. This prevents installation failures while keeping the corporate tax report working through existing account tags.
Original PR description
This commit removes the tax return type built on the Bangladesh tax report, which no longer exists. Before: `bd_tax_return_type` resolved `l10n_bd.tr_form` through a `ref`. That report did not match the NBR reporting format and is dropped from the community module, so the reference would fail to resolve and take the whole module down with it at install. Now: the return type and the data file holding it are gone. The corporate tax report is untouched; it reads through the `account_tags_tax_c_exempt`, `c_cog` and `c_exp` account tags rather than through `tr_form`, and those tags are kept on the accounts in the community module. task-6219535
AI image generation and editing now receives the product or website image context that was previously being dropped when opening the media dialog. This prevents the AI assistant from asking for information it should already know and helps it generate results based on the correct product image.
Original PR description
When opening the media dialog from a product image (avatar) edit button, the `ImageFieldWithMediaDialog` (patched by the `ai`) attempts to pass `record`, `originalRecordModel`, and `originalRecordId` as props However, since these props are not declared in `mediaDialogProps`, so prop validation filters them out of `this.props`. Thus they are `undefined` in the MediaDialog. Thus they are not passed to the AI chat launcher. Thus ai has to ask user about product name. This commit registers these props on the `mediaDialogProps` schema in the `ai module patch, preventing Owl from stripping them. task-6365588 **Community counterpart: https://github.com/odoo/odoo/pull/273676** Forward-Port-Of: odoo/enterprise#123050
This fix ensures that when a payment is unreconciled, the related cash basis tax reversal is dated in the same reporting period as the original tax entry. This prevents tax reports from showing mismatched amounts across different months, keeping period totals accurate.
Original PR description
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax…
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax report nets to zero for that period. Steps to reproduce: - Enable cash basis and create a cash basis tax (exigibility on payment) - Post an invoice dated in the past with that tax - Reconcile a bank statement line to the invoice - Resequence the cash basis entry so the month is dropped from the name (CABA/08/2026/0001 -> CABA/2026/0001) - Unreconcile the statement line Issue: The reversal CABA entry created on the last unreconcile is dated today instead of the origin entry's month. In the tax report the original tax amount stays in the statement's month while the reversal amount appears in the current month, so the two no longer cancel out. Analysis: While under a monthly journal sequence a past date returns the last day of that month, under a yearly sequence a past date within the current year returns the latter between the move date and today, moving the reversal out of the origin period. opw-6301553 Forward-Port-Of: odoo/odoo#284840 Forward-Port-Of: odoo/odoo#281250
Vendor bills can now correctly create assets that do not require depreciation. This removes an unnecessary restriction, helping accounting teams record these assets without manual workarounds.
Original PR description
This commit fixes the ability to create no depreciation assets from vendor bills. Previously, a condition on the depreciation account and expense account restricted the asset creation. backport of https://github.com/odoo/enterprise/pull/123070 task-6283929 opw-6540498 Forward-Port-Of: odoo/enterprise#130653
Invoices linked to Malaysian MyInvois documents now continue to show their final status when a submission is cancelled or invalid. This avoids confusion where invoices appeared as if they had never been sent, while still allowing users to resend when appropriate.
Original PR description
`l10n_my_edi_state` on the invoice was left empty whenever the related document wasn't in an "active" state (`valid`/`rejected`/`in_progress`), even once it reached a terminal outcome like…
`l10n_my_edi_state` on the invoice was left empty whenever the related document wasn't in an "active" state (`valid`/`rejected`/`in_progress`), even once it reached a terminal outcome like `cancelled` or `invalid`. As a result, the form and list view showed no MyInvois status at all for those invoices, making them look like they were never sent to MyInvois - even though the underlying `myinvois.document` clearly was. Instead of deriving `l10n_my_edi_state` purely from the active document, fall back to a cancelled/invalid document when no active one exists, so the invoice always reflects its real MyInvois status. Since the state can now be `invalid`/`cancelled` (truthy) instead of always empty, every place that used to treat a falsy `l10n_my_edi_state` as license to send/resend is updated to also allow `invalid`/`cancelled`, since those are equally free to resend: `_compute_show_reset_to_draft_button`, `action_l10n_my_edi_send_invoice`, and the "Send To MyInvois" button/field visibility in the view. `l10n_my_invoice_need_edi` also used `l10n_my_edi_state` in its computation, purely to exclude `rejected` invoices (which still have an active document blocking a new send). That state check is now handled directly by each caller instead: the field is renamed to `l10n_my_edi_is_applicable` and reduced to the structural question of whether MyInvois applies to the invoice at all (posted, MY, proxy user configured), while `_compute_highlight_send_button` checks `l10n_my_edi_state != 'rejected'` explicitly. task-6442567
Warehouse moves created by push rules now use the most precise source location available, including sublocations when relevant. This helps stock transfers and picking records better reflect where items actually moved from, reducing location mismatches in inventory operations.
Original PR description
This PR fixes the moves (and their picking) `location_id` to be more accurate after applying push rules on the moves. Before this PR: The new move introduced from push rules (and its new picking)…
This PR fixes the moves (and their picking) `location_id` to be more accurate after applying push rules on the moves. Before this PR: The new move introduced from push rules (and its new picking) would have the `location_id = old_move.location_dest_id` which could be a parent of the location_dest_id of the old move_lines which is more accurate. For example, if a move has a `location_dest_id = 'stock'` and the user changes the move_lines of that move so that `move_lines.location_dest_id = 'stock/sublocation'`. When push rules are applied on the move_lines, `new_move.location_id = old_move.location_dest_id = 'stock'`. But, it should be `new_move.location_id = 'stock/sublocation'`. After this PR: The new move now (and its picking) will have the `location_id` set as the `rule.location_src_id` or `old_move.location_dest_id` just in case the `old_move.location_dest_id` is a sublocation of `rule.location_src_id` which gives a more accurate source location for the move and the picking. Task-6226781
This fixes an inventory issue where moving stock stored in a package that was already reserved for delivery could fail with an access error. Warehouse users can now relocate these packaged quantities without being blocked, improving day-to-day stock management reliability.
Original PR description
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track…
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track localization in settings - Create a new storable product. - Using an inventory adjustment, add some quantity of that product in Stock, in a new package X. - Create a sales order for that product and confirm it, so the quantity in Stock gets reserved. - Go to Inventory > Reporting > Locations. - Select the quant and try to relocate it, e.g. to WH/Input. -> Raises an AccessError: "Failed to write field stock.package.picking_ids" **Cause** Relocating a quant creates a move: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1531 which creates a new move line without a `picking_id`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1276-L1287 Since both move lines (the new one and the one linked to the SO delivery) share the same `result_package_id`, in `_compute_picking_ids`, both move lines are grouped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L174-L176 Thus, two "pickings" end up associated with the package: the SO's, and `None`. While setting those pickings on the package, it tries to access them: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L182 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1497-L1503 And since `self` isn't just `None`, this check won't be skipped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4152 This eventually raises an AccessError since `None` gets filtered out by `filtered_domain`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4154-L4156 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1504-L1505 opw-6427070 Forward-Port-Of: odoo/odoo#283937 Forward-Port-Of: odoo/odoo#282209
This fixes an issue where changing a subcontracting manufacturing order from a later delivery could incorrectly update quantities on earlier deliveries. Each new subcontracting order is now linked only to its own delivery, helping keep purchase and delivery records accurate.
Original PR description
**STEP TO REPRODUCE** 1. Create a purchase order for a subcontracted product. 2. validate the picking. 3. Return to the PO, and increase the purchased qty and save, this should create a new picking. 4. On the new picking, click on the smart button to see the subcontracting MO details. 5. Change the product quantity on the MO and save. 6. Return to the first picking, and notice the delivered quantity was changed, this should not be the case. **CAUSE** When creating a new MO, its `move_finished_ids` is linked to the moves of all previous pickings when we create the MO. It should only be linked to the new picking move. opw-6320704 Forward-Port-Of: odoo/odoo#284605 Forward-Port-Of: odoo/odoo#271556
Map location lookups now finish saving even if a user leaves the map view before the lookup completes. This avoids losing coordinates and reduces repeat requests to external, rate-limited location services.
Original PR description
Prevents discarding geocoded coordinates when navigating away from the map view before asynchronous requests complete. Previously, caching calls used a component-scoped ORM service that threw an uncaught "Component is destroyed" error upon unmount. Decoupling the caching write from the component's lifecycle preserves fetched coordinates and avoids duplicate rate-limited API requests. task-6535656
Restaurant orders edited on one device will no longer be skipped during synchronization after another device refreshes the same table. This helps ensure added order lines are saved and shared correctly across point-of-sale devices, reducing the risk of lost sales or service errors.
Original PR description
Steps to reproduce: - Device A opens table 10 and adds a product - Device B opens table 10, then goes back to the floor plan - Device A goes back to the floor plan => The lines added on A are lost, they are never synced to the database, nor to the other device When B triggers a synchronisation, A reads the open orders from the server. The local lines of A are kept, but `setup`, which is also called when a record is updated, resets the dirty flag of the order. Going back to the floor plan calls `syncAllOrders`, which filters the order out because it is not dirty anymore, so the lines are never sent. The dirty flag is now kept when the record is updated with server data, since that data does not contain the changes made locally and must be synced in the next synchronization call task-id: 6486258 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286279 Forward-Port-Of: odoo/odoo#284701
Early payment discount entries now preserve the original invoice line cost allocation for all discount calculation methods. This prevents accounting details from being lost when invoices with mixed or excluded discount handling are paid early.
Original PR description
_get_invoice_counterpart_amls_for_early_payment_discount_per_payment_term_line only split the early payment discount amount per invoice line (and thus preserved each line's own analytic distribution)…
_get_invoice_counterpart_amls_for_early_payment_discount_per_payment_term_line only split the early payment discount amount per invoice line (and thus preserved each line's own analytic distribution) when the payment term's early_pay_discount_computation was "included". For "mixed" and "excluded", it instead built a single counterpart line, discarding the analytic distribution of the invoice lines. Compute the per-invoice-line base amounts (and the corresponding price_unit for "mixed") for all three computations, keeping only the tax calculation restricted to "included". This way "mixed" and "excluded" discount entries now keep the analytic distribution of the invoice line they come from, while behavior for "included" is unchanged. Steps: - Create 3 payment terms with EPD, each one with a different `early_pay_discount_computation` setting - For each payment terms, create one invoice with two lines, only one having an analytic distribution - Confirm and pay the 3 invoices (early payment, discount applied) Issue: For 'mixed' and 'excluded', the discount line is the sum of the discount from the 2 invoice lines, and the analytic distribution is lost. opw-6380278 Forward-Port-Of: odoo/odoo#286816 Forward-Port-Of: odoo/odoo#282541
VoIP text-to-speech sounds now have to be generated before they can be saved, ensuring the saved text and voice match the actual audio users will hear. This prevents incomplete or outdated audio settings from being saved and makes call flow setup more reliable.
Original PR description
Creating a text-to-speech sound did not require its audio to be generated. It was therefore possible to save a sound whose text and voice did not match any audio file. Moreover, the Generate object button saved the form before generating the audio. This required specific reload logic in call flow dialogs and could persist incomplete values prematurely. Generate the audio as a preview and update the form's draft with the result instead of saving the record through an object action. Store the text and voice used for the generation, and prevent saving when they no longer match the current values or when no generated audio is present. Also discard an asynchronous generation result if the text or voice was changed while the request was running, and remove the now-unnecessary call flow reload logic.
Accounting users who are not administrators can now view and select email templates when sending manual payment reminders. This prevents an access error in the reminder flow, helping teams send customer follow-ups without needing admin permissions.
Original PR description
**Steps to reproduce:**
1. Log in as demo user
2. Go to Accounting > Customers > Invoices
3. Select at least one invoice and click on "Send Reminder"
4. Attempt to change or view the available choices in the "Email Template" field.
**Issue:**
An AccessError is raised:
`You are not allowed to access 'Model' (ir.model) records.`
**Cause:**
The view domain on `template_id` was set to `[('model_id.model', '=', 'res.partner')]`. Traversing `model_id.model` forces the ORM to evaluate security permissions on the `ir.model` relation, which fails for non-admin users.
opw-6452738
Forward-Port-Of: odoo/enterprise#129966This fixes Saudi localization issues around additional customer identifiers used for electronic invoicing. Businesses can now better record required buyer IDs, including for non-Saudi contacts, helping invoices meet Saudi compliance requirements.
Original PR description
This commit fixes the issues with multi id implementation of `l10n_sa_edi`. Before this PR: - The additional identifiers for `l10n_sa` were declared in `account`. - The `slice` function in js had wrong arguments, `slice(10)`. - `l10n_sa_invoice_type` was not getting updated when the company status (`is_company`) of the partner was updated. - `SA_OTH` additional_identifiers was not visible to non-saudi contacts. After this PR: - The additonal identifiers are now moved into `l10n_sa`. - The `slice` function has been removed. - Added an onchange to populate Saudi Arabia TIN. - Add test cases for l10n_sa additional identifiers - Add test cases for l10n_sa_edi additional identifiers - Added `copy=False` for `l10n_sa_invoice_type` and added `is_company` in dependencies - `SA_OTH` is now visible to non saudi contacts. task-6413445 task-6319117 Forward-Port-Of: odoo/odoo#270776
This fix ensures Saudi Arabia tax tag updates are applied during Odoo 19 upgrades. It prevents VAT tax grids and invoices from keeping outdated tax tags, reducing the need for manual correction after migration.
Original PR description
**Issue:** During migration to Odoo 19, the SA tax tag migration logic is present in: "migrations/2.1/pre-migrate.py" was not executed during the migration of databases because this: * SA compact tax…
**Issue:** During migration to Odoo 19, the SA tax tag migration logic is present in: "migrations/2.1/pre-migrate.py" was not executed during the migration of databases because this: * SA compact tax tags were not renamed * VAT tax grids still use old tags * Localization reload was partially updating taxes * manual intervention was required Added logs/debugging in: * 2.1/pre-migrate.py * 2.1/end-migrate.py * 2.2/end-migrate.py - Verified in both local and customer databases that only the 2.2 migration path was executed during upgrade, while the 2.1 migration scripts were skipped because the Odoo 19 manifest upgrade path already targeted the 2.2 migration version. - 19:https://github.com/odoo/odoo/blob/67510fd36f7af31f83ef110602f9922ed5a43024/addons/l10n_sa/__manifest__.py#L6 **Solution:** - Bumped the l10n_sa module version to 2.3 to apply these changes to databases that are already in production. - Moved `migrations/2.1/pre-migrate.py` to `migrations/2.3/pre-migrate.py` to ensure the SA tax tag migration logic is executed during the `2.3` upgrade flow. - Moved `migrations/2.2/end-migrate.py` to `migrations/2.3/end-migrate.py` to align with the `2.3` version bump and refresh the SA tax mappings correctly during migration. * SA tax tags migrate correctly * VAT tax grids update automatically * invoices use new tags correctly * No manual localization reload required OPW - [6117408](https://www.odoo.com/odoo/project/70/tasks/6117408), [6224788](https://www.odoo.com/odoo/project/70/tasks/6224788) 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#264571
Fixed an issue that could leave Ecuadorian electronic invoicing withholding totals blank in the tax totals widget. This helps users see the expected withholding amounts reliably and avoids errors when reopening or refreshing the document view.
Original PR description
TaxTotalsComponent now declares its totals as a computed, so formatData runs inside that computed's own getter. TaxTotalsComponentForWithhold still overrides formatData to assign this.totals directly, which overwrites the computed from within it: the getter returns undefined, so the table's t-if hides it and the widget renders empty, and every later read raises "this.totals is not a function" because this.totals is now a plain object. From: odoo/odoo#270245. Forward-Port-Of: odoo/enterprise#130691
This change rolls back a recent database connection handling update because it caused connection usage to spike on gevent servers. Reverting it helps stabilize server resource usage and reduces the risk of performance issues for users relying on real-time features.
Original PR description
This reverts commit 11e76ba8042648596dc0a7c731a78dd786e36884 and PR #285423 The connection usage on the gevent server is spiking. Forward-Port-Of: odoo/odoo#287008
This fixes an error that could stop Website SEO content from being updated with AI when the AI agent used certain document sources. Businesses can now use those documents reliably in AI-assisted SEO updates without the process crashing.
Original PR description
### Problem When an AI agent includes a document source whose underlying `ir.attachment` has `res_field` set (e.g., an invoice PDF generated from a Sale Order), triggering **Update With AI** from…
### Problem
When an AI agent includes a document source whose underlying `ir.attachment`
has `res_field` set (e.g., an invoice PDF generated from a Sale Order),
triggering **Update With AI** from **Website → Site → Optimize SEO**
raises a `KeyError` in `_build_rag_context`.
### Steps to Reproduce
1. Open the **AI** app.
2. Configure the **Odoo Agent**.
3. Add a source → **Add From Documents**.
4. Select a document whose underlying `ir.attachment` has `res_field` set
(e.g., an invoice PDF generated from a Sale Order).
5. Go to **Website → Site → Optimize SEO**.
6. Click **Update With AI**.
7. Observe the following error:
```
KeyError: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```
[Video](https://drive.google.com/file/d/1AAg0AvC3TRQzMLhI9hWFuSarOTHzQE-b/view?usp=sharing)
### Root Cause
In `ai/models/ai_agent.py`, `_build_rag_context()` retrieves the
`ai.agent.source` records corresponding to the embeddings by searching on the
attachment checksum:
```python
agent_sources = self.env["ai.agent.source"].search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
```
This domain traverses `attachment_id.checksum`, which internally calls
`ir.attachment._search()`.
As part of the standard attachment search behavior,
`ir.attachment._search()` automatically injects a
`('res_field', '=', False)` filter unless:
- `skip_res_field_check=True` is set in the context,
- the domain explicitly references `id` or `res_field`, or
- `bypass_access` is enabled.
```python
domain = Domain(domain)
if (
not self.env.context.get("skip_res_field_check")
and not any(d.field_expr in ("id", "res_field") for d in domain.iter_conditions())
and not bypass_access
):
disable_binary_fields_attachments = True
domain &= Domain("res_field", "=", False)
```
[Reference](https://github.com/odoo/odoo/blob/19.0/odoo/addons/base/models/ir_attachment.py#L630)
Because of this implicit filter, attachments with `res_field` set are excluded
from the search. Consequently, `agent_sources` does not contain all the sources
corresponding to the retrieved embeddings.
Later, `_build_rag_context()` builds a checksum-to-source mapping:
```python
source_map = {
source.attachment_id.checksum: source
for source in agent_sources
}
for embedding in similar_embeddings:
checksum = embedding.attachment_id.checksum
agent_source = source_map[checksum]
```
Since `source_map` is built from the incomplete `agent_sources` recordset, it is
missing entries for attachments filtered by `ir.attachment._search()`.
However, `similar_embeddings` still contains embeddings for those attachments.
As a result, the lookup:
```python
agent_source = source_map[checksum]
```
raises a `KeyError`.
### Solution
Bypass the implicit `res_field` filter when searching `ai.agent.source`:
```python
agent_sources = (
self.env["ai.agent.source"]
.with_context(skip_res_field_check=True)
.search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
)
```
This ensures that all `ai.agent.source` records matching the requested
attachment checksums are returned, including those referencing attachments with
`res_field` set. As a result, `source_map` contains all expected entries and
`_build_rag_context()` no longer raises a `KeyError`.
opw-6379816
Forward-Port-Of: odoo/enterprise#129999
Forward-Port-Of: odoo/enterprise#125780This fixes a website shop error that could occur when all variants of a product were deleted. The shop now falls back to the main product information for unit pricing, so customers can continue browsing without encountering an error.
Original PR description
Currently, an error occurs when user deletes all the variants of a product and opens the website raises an error. Steps to replicate: - Install `website_sale`, open settings and turn on `Product…
Currently, an error occurs when user deletes all the variants of a product and opens the website raises an error. Steps to replicate: - Install `website_sale`, open settings and turn on `Product Reference Price` and `Product Variants`. - Create a new product `test` , give an attribute and 2 values. - Click on the `Variants` smart button, select all and delete. - Publish this produce in website and open shop on website. Error: ``` ValueError: Expected singleton: product.product() ``` Cause: - Issue occurs after a recent addition of a feature that shows product's unit price on website (check [PR]). - As the user deleted all the variants of the product `test`, when trying to get the unit price of the product's variants [1] causes this error to occur. Solution: - If the product doesnt have any variants we should get the unit price from the product template itself. [PR]: https://github.com/odoo/odoo/pull/254309 [1]: https://github.com/odoo/odoo/blob/22ede9cd601f54ebf248a7ddace433746f066e68/addons/website_sale/models/product_template.py#L686 sentry-7635385579 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282945
This fix ensures mail-related background data is cleaned up whenever an Odoo app is closed, including apps like Point of Sale and self-ordering that embed mail features. It prevents stale callbacks from accumulating over time, reducing memory use in affected test scenarios by around 38%.
Original PR description
ChatHub's cleanup (the shared `subscribeToStorage` listener) only ran when something explicitly called `store._runDisposeFns()`. Only mail's own test helper did that; any other app embedding…
ChatHub's cleanup (the shared `subscribeToStorage` listener) only ran when something explicitly called `store._runDisposeFns()`. Only mail's own test helper did that; any other app embedding `mail.store` (point_of_sale, pos_self_order, ...) never disposed it, so `subscribeToStorage` kept one stale callback per undisposed ChatHub, unbounded. Hook disposal into the services registry's "CLEANUP" event instead, which Owl already fires on every app teardown regardless of embedding (same pattern as `profiling_service.js`). Converting `storeService` to an Owl plugin using its own `onWillDestroy` (see TODO) would be the cleaner long-term fix. Measured via hoot's [MEMINFO] on the self_order_service suite that exposed this leak, same db/commit, only the fix toggled: | suite | before (MB) | after (MB) | % | |---------------------------------------------------------|------------:|-----------:|-------:| | pos_self_order/services/self_order_service (30 tests) | 253.8 | 155.6 | -38.7% | | pos_online_payment_self_order/self_order_service | 253.1 | 155.0 | -38.8% | | pos_self_order_event/self_order_service | 253.2 | 155.1 | -38.7% | | pos_self_order_iot/self_order_service | 260.1 | 162.0 | -37.7% | | tests done (final) | 251.9 | 153.8 | -38.9% | --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr