Daily updates from Odoo
Wednesday, August 5, 2026
16 changes · saas-18.3
Resolved issues and error corrections
This fixes an issue where quickly clicking to expand a report line could create duplicate entries and stop the line from collapsing correctly. Financial reports now behave consistently even when users click quickly or have a slow network connection.
Original PR description
Steps to reproduce:- - Open any report. - Click on a particular line to unfold more than once very fast(or throttle network to 3G) - Once line is unfolded click again to fold that line. - Line is not…
Steps to reproduce:- - Open any report. - Click on a particular line to unfold more than once very fast(or throttle network to 3G) - Once line is unfolded click again to fold that line. - Line is not folding. Cause:- - When we clicked multiple times to unfold line, duplicate child lines were created(as many times as many times we clicked). - Because when first promise was not resolved so `unfolded = false` and we clicked again so new promise also tries to unfold the same line, resulting in unfolding the same line multiple times. - In version 17.0 these duplicate child lines are created but somehow not visible but it breaks `foldLine`. From version 18.0 onwards these duplicate child lines are visible. Solution: In `unfoldLine` set the flag `unfolding`. So in all clicks other than first, we get `unfolding = true` and don't proceed further, preventing unfolding the same line multiple times. task-6260425 Forward-Port-Of: odoo/enterprise#126504 Forward-Port-Of: odoo/enterprise#120392
This fix prevents an optional worksheet guide from interrupting an automated field service report test when demo data is absent. It makes testing more reliable without changing day-to-day user functionality.
Original PR description
When there is only one worksheet, the ‘Explore Worksheets Using an Example Template’ wizard opens. Because of this, the test fails without demo data. If we add steps for this wizard, it won’t open when there is more than one worksheet, which will again cause the test to fail. Also, we cannot add this conditon on step. Therefore, to ignore this wizard, i created a worksheet before running the tour so that the wizard does not open. backport of https://github.com/odoo/enterprise/commit/5bb2d96087f50f7df1d51bbd4c31bb23b6d313da runbot-242471 Forward-Port-Of: odoo/enterprise#124885
Users can now duplicate several maintenance requests at once without the system showing an error. This prevents interruptions when managing maintenance work in bulk and makes the request list action behave as expected.
Original PR description
Currently, when a user attempts to duplicate multiple maintenance requests simultaneously, the system throws a ValueError (Expected singleton). This PR fixes that. ### How to reproduce the issue: - Install `mrp_maintenance` module; - Open maintenance request list view; - Select multiple records and try to duplicate them using the Action button; - It will throw a traceback stating a singleton error. ### Expected behavior after PR is merged: Now multiple maintenance requests will be copied without raising any errors. Forward-Port-Of: odoo/enterprise#124255
Planning analysis now includes slots assigned to fully flexible employees who do not have a fixed working schedule. This ensures managers see a complete view of planned work and staffing in timesheet and planning reports.
Original PR description
Steps to reproduce: ------------------- 1. Install project_timesheet_forecast. 2. Create a fully flexible employee (without a working schedule). 3. Create a planning slot. 4. Open the Timesheet/planning Analysis report. Issue: ------ Planning slots for fully flexible employees are not included in the report. Cause: ------ https://github.com/odoo/enterprise/blob/7d4b43cfa1934856d41992cbe8242eaf62575c2c/project_timesheet_forecast/report/timesheet_forecast_report.py#L142-L161 The report assumes every resource has a working schedule and only considers resources with a resource calendar. As a result, resources without a calendar are excluded from the report. Solution: --------- Handle resources without a working schedule separately so that planning slots for fully flexible employees are also included in the report. opw-6361571 Forward-Port-Of: odoo/enterprise#125072
The barcode batch picking test flow now waits for the first scanned item quantity to update before selecting the next line. This prevents quantities from being assigned to the wrong product line during rapid scanning, helping ensure batch picking remains accurate and reliable.
Original PR description
Problem: When scanning the first product, the second move line is clicked immediately after.…
Problem: When scanning the first product, the second move line is clicked immediately after. https://github.com/odoo/enterprise/blob/bfb8bab7636a84c829f16087771e00bf31ad2cce/stock_barcode_picking_batch/static/tests/tours/tour_test_barcode_batch_flows.js#L1529-L1541 If this happens before the first scan has finished, its quantity is incorrectly applied to the second move line that is clicked. This causes a 0 - 3 split instead of a 1 - 2 split, which results in there only being 6 move lines instead of 7. We updated our quantity on the `currentLine` https://github.com/odoo/enterprise/blob/bfb8bab7636a84c829f16087771e00bf31ad2cce/stock_barcode/static/src/models/barcode_model.js#L1478 When finding `currentLine`, we go through `_findLine` and use `this.selectedLineVirtualId`, which is the one that is currently selected in the UI https://github.com/odoo/enterprise/blob/bfb8bab7636a84c829f16087771e00bf31ad2cce/stock_barcode/static/src/models/barcode_model.js#L1705-L1712 Purpose: By adding this step, we wait until the first line’s quantity to be updated before it moves on and clicks on the second product. runbot-941211 Forward-Port-Of: odoo/enterprise#124162
## Issue When generating an invoice for a service product with an invoicing policy based on timesheets, the amount of hours invoiced is wrong if there exists a credit note linked to that SO. ## Steps to reproduce 1. Install *Sales Timesheet* (`sale_timesheet`) 2. Create a Product P - Product Type: Service - Create on Order: Task - Project: Any project - Invoicing Policy: Based on Timesheets 3. Create a SO - Any Customer - Product P (any quantity) - Confi
Original PR description
## Issue When generating an invoice for a service product with an invoicing policy based on timesheets, the amount of hours invoiced is wrong if there exists a credit note linked to that SO. ## Steps…
## Issue
When generating an invoice for a service product with an invoicing policy based on timesheets, the amount of hours invoiced is wrong if there exists a credit note linked to that SO.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P
- Product Type: Service
- Create on Order: Task
- Project: Any project
- Invoicing Policy: Based on Timesheets
3. Create a SO
- Any Customer
- Product P (any quantity)
- Confirm the SO
4. In the created task, add a timesheet entry
- Date: Today
- Time Spent: 10:00 (10 hours)
5. Create and confirm the invoice for the SO
6. Create a Credit Note from the invoice, set the quantity to 4 hours, and confirm it
7. From the created task, add a second timesheet entry
- Date: Any future date (e.g., today + 7)
- Time Spent: 15:00 (15 hours)
8. Create a second invoice, but set a Timesheets Period that only covers the second timesheet entry
9. **The quantity on the newly created invoice is 9 hours, even though we're clearly trying to invoice the 15 hours from the second timesheet entry.**
## Cause
The second invoice is impacted by the credit note generated from the first one. When generating that second invoice, the [`_recompute_qty_to_invoice`](https://github.com/odoo/odoo/blob/0512ebd4c8cc277fdb4fbe0f57619a0bd61192c2/addons/sale_timesheet/models/sale_order_line.py#L149) method incorrectly computes the amount to invoice by taking into account `account.analytic.line` from outside the provided range.
The delivered quantity is correctly calculated by taking into account the provided range (through the `start_date` and `end_date` added to the domain passed to `_get_delivered_quantity_by_analytic`:
https://github.com/odoo/odoo/blob/0512ebd4c8cc277fdb4fbe0f57619a0bd61192c2/addons/sale_timesheet/models/sale_order_line.py#L176-L180
But then, for each `sale.order.line`, we look at the related `account.analytic.line` without taking into account the provided dates, which leads to lines outside of the range impacting the invoice.
https://github.com/odoo/odoo/blob/0512ebd4c8cc277fdb4fbe0f57619a0bd61192c2/addons/sale_timesheet/models/sale_order_line.py#L182-L193
In the example described in the *Steps to reproduce*, we start with a (correct) amount delivered of 15.0, we find two `invoice_lines_to_calculate` (the invoice of 10 hours, and the credit note of 4 hours), which leads to the quantity to invoice being set to `15 - (-4 + 10) = 9`. This seems like an odd behavior as it:
- doesn't invoice all the hours within the provided range (15 hours within the range, and we're only invoicing 9)
- doesn't invoice **all** the hours left to be invoiced (6 hours are already invoiced, 25 should be in total, and we're invoicing 9)
opw-6373870
Forward-Port-Of: odoo/odoo#277727Steps to reproduce: - Install 'Sales', 'Accounting' and 'l10n_sa_edi' - Settings > Accounting > Rounding Method > Round Globally - Create an invoice whose per-line tax base is fractional (e.g. a price-included 15% VAT, 3 lines at 10.00 -> base 8.6957 each) - Generate the ZATCA UBL document Issue: The exported document is internally inconsistent and is rejected by ZATCA (BR-CO-13): cbc:LineExtensionAmount (BT-106) = 26.10 while cbc:TaxExclusiveAmount (BT-109), and thus the QR / PayableAmo
Original PR description
Steps to reproduce: - Install 'Sales', 'Accounting' and 'l10n_sa_edi' - Settings > Accounting > Rounding Method > Round Globally - Create an invoice whose per-line tax base is fractional (e.g. a…
Steps to reproduce: - Install 'Sales', 'Accounting' and 'l10n_sa_edi' - Settings > Accounting > Rounding Method > Round Globally - Create an invoice whose per-line tax base is fractional (e.g. a price-included 15% VAT, 3 lines at 10.00 -> base 8.6957 each) - Generate the ZATCA UBL document Issue: The exported document is internally inconsistent and is rejected by ZATCA (BR-CO-13): cbc:LineExtensionAmount (BT-106) = 26.10 while cbc:TaxExclusiveAmount (BT-109), and thus the QR / PayableAmount (BT-115), = 26.09. This is the same 0.01 discrepancy reported for 100% down-payment invoices under global rounding. Cause: LineExtensionAmount was built by summing account.move.line.price_subtotal, which is always rounded per line (8.70 x 3 = 26.10), whereas TaxExclusiveAmount is built from the aggregated base_amount_currency, which follows the company rounding method and is rounded globally (26.087 -> 26.09). Under 'round_globally' the two diverge by a cent. This is the base-amount counterpart of commit 3d398789, which aligned the prepaid tax amount to global rounding but left the line net amount on per-line rounding. Solution: Derive the line net amount from the globally-rounded aggregated base (taxes_vals['base_amount_currency']) https://github.com/odoo/odoo/blob/c7c361e6af4da43dc1f9653703067ffe6500a046/addons/account/models/account_tax.py#L1529 instead of the per-line rounded price_subtotal, consistent with total_amount_sa on the same line. The whole document now stays on a single rounding basis, so the sum of the line net amounts equals the TaxExclusiveAmount and BR-CO-13 is satisfied. opw-5881564 Forward-Port-Of: odoo/odoo#275093
Steps to reproduce: ------------------- 1. Install `hr` module 2. Create two employees (e.g., emp1 and emp2) and assign emp1 as the manager of emp2 → emp2 is now a direct subordinate of emp1 3. In the employee list view, apply the custom filter "Direct subordinates is set" (child_ids != False) → emp1 appears in the results as expected 4. Archive emp2 5. Apply the same filter again → emp1 still appears in the results even though it has no active subordinates Issue
Original PR description
Steps to reproduce: ------------------- 1. Install `hr` module 2. Create two employees (e.g., emp1 and emp2) and assign emp1 as the manager of emp2 → emp2 is now a direct subordinate of emp1 3. In…
Steps to reproduce: ------------------- 1. Install `hr` module 2. Create two employees (e.g., emp1 and emp2) and assign emp1 as the manager of emp2 → emp2 is now a direct subordinate of emp1 3. In the employee list view, apply the custom filter "Direct subordinates is set" (child_ids != False) → emp1 appears in the results as expected 4. Archive emp2 5. Apply the same filter again → emp1 still appears in the results even though it has no active subordinates Issue: ------ When an employee (e.g., `emp2`) is archived, their manager (`emp1`) should no longer appear in the "Direct subordinates is set" (child_ids != False) filter — since `emp1` no longer has any active subordinates. However, `emp1` still appears in the search results after `emp2` is archived, because the underlying EXISTS subquery checks all subordinates regardless of their active state. Cause: -------- Before this commit 5ef007a, `osv.expression`, filtering on a One2many field would automatically search against [active co-records ](https://github.com/odoo/odoo/blob/5f65e92d7fa341193df53f5aba1620b596f9a1ec/odoo/osv/expression.py#L1260-L1265)only by default. After that commit, the `condition_to_sql` method in `_RelationalMulti` constructs the comodel with [active_test=False](https://github.com/odoo/odoo/blob/463ca4cf867812890c17d1e1abf7640b04f70ad0/odoo/orm/fields_relational.py#L672-L686) when resolving relational field conditions. This causes the EXISTS subquery generated for `child_ids != False` to compare against all subordinates. (including archived ones rather than active ones only). Solution: --------- Added a callable `domain` attribute on the `child_ids` field definition so that only active subordinates are considered by default. This ensures [get_comodel_domain()](https://github.com/odoo/odoo/blob/2d8b24a791b6fe6bb214c32d4fb58b3d46eca70b/odoo/orm/fields_relational.py#L75-L85) returns a server-side domain that filters out archived subordinates, making the `child_ids != False` filter behave as expected. **NOTE:** > The ORM uses the **active_test** flag when doing searches. Having in [1, 2, 3] in domains bypasses the search method because we suppose that we already searched to find these ids. For hierarchical resolution, we bypass rights and active_test. The result is that inactive records are considered in the result. confirmed with the framework team, and it is intended behaviour. However, this is not the expected behaviour for the direct subordinates case in `hr`. The fix is therefore applied at the field level by explicitly declaring a domain on `child_ids` to filter out archived subordinates. ORM commit: https://github.com/odoo-dev/odoo/commit/12eae5c85fa7facb299f0f1bf1fdd62e3ff82aa5 opw-6193104 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This commit fixes the verification JSON. For OSS taxes no_sujeto_loc and no_sujeto, CuotaTotal and ImporteTotal must Only include the base amount, not the tax amount. See the chatter in the task for AEAT guidelines. Also removed the validation error blocking no_sujeto_loc taxes with a non-zero amount, since OSS taxes legitimately have one in Odoo accounting (e.g. 22% IT VAT) even though it is excluded from the Veri*Factu json. upgrade :- https://github.com/odoo/upgrade/pull/10799 tas
Original PR description
This commit fixes the verification JSON. For OSS taxes no_sujeto_loc and no_sujeto, CuotaTotal and ImporteTotal must Only include the base amount, not the tax amount. See the chatter in the task for AEAT guidelines. Also removed the validation error blocking no_sujeto_loc taxes with a non-zero amount, since OSS taxes legitimately have one in Odoo accounting (e.g. 22% IT VAT) even though it is excluded from the Veri*Factu json. upgrade :- https://github.com/odoo/upgrade/pull/10799 task-5411766 Forward-Port-Of: odoo/odoo#279541 Forward-Port-Of: odoo/odoo#272068
Currently, if you have a partner with Belgian VAT as peppol eas, but no peppol endpoint, you get a traceback when you open the Send&Print. It can happen easily, if you have customers without VAT or company registry, that were created 2 years ago, when we put Belgian VAT as default. 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
Original PR description
Currently, if you have a partner with Belgian VAT as peppol eas, but no peppol endpoint, you get a traceback when you open the Send&Print. It can happen easily, if you have customers without VAT or company registry, that were created 2 years ago, when we put Belgian VAT as default. 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#280234
By default event notifications are configured with positive integers to state how many minutes/hours/days they should be triggered before an event starts. This works fine in within Odoo. But in an ICS file a TRIGGER with such positive integer means it starts after the event. For example ICS file generated by Odoo contains TRIGGER;related=START:PT15M but it should be negative: TRIGGER;related=START:-PT15M (note the minus) in order to trigger before the event. See [here](https://icalendar.org/iCal
Original PR description
By default event notifications are configured with positive integers to state how many minutes/hours/days they should be triggered before an event starts. This works fine in within Odoo. But in an ICS file a TRIGGER with such positive integer means it starts after the event. For example ICS file generated by Odoo contains TRIGGER;related=START:PT15M but it should be negative: TRIGGER;related=START:-PT15M (note the minus) in order to trigger before the event. See [here](https://icalendar.org/iCalendar-RFC-5545/3-8-6-3-trigger.html) for reference. Current behavior before PR: The reminder is triggered AFTER the event start Desired behavior after PR is merged: The reminder is triggered BEFORE the event start Closes #245052. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274744
The current vacuum only collects `ir_sequence` from closed sessions but doesn't take into account "orphaned" sequences, such as sequences which belongs to `pos.session` that have been deleted. We also need to clean those to avoid having too many Postgres sequences, especially since it's limited to 10K on Odoo.sh. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263952 Forward-Port-Of: odoo/odoo#261777
Original PR description
The current vacuum only collects `ir_sequence` from closed sessions but doesn't take into account "orphaned" sequences, such as sequences which belongs to `pos.session` that have been deleted. We also need to clean those to avoid having too many Postgres sequences, especially since it's limited to 10K on Odoo.sh. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263952 Forward-Port-Of: odoo/odoo#261777
Steps to reproduce: - Install employees and attendance app - Make sure there are 2 companies - Make user's employee record for Company B, but not A - Make company A the default company for user - Enable "attendances from backend" setting - Click on the attendance dot (systray) Current Behavior: The dot disappears and you can't check in Expected Behavior: You are able to check in Other bug scenario: If you have employee records in both Company A and Company B, you can check in.
Original PR description
Steps to reproduce: - Install employees and attendance app - Make sure there are 2 companies - Make user's employee record for Company B, but not A - Make company A the default company for user - Enable "attendances from backend" setting - Click on the attendance dot (systray) Current Behavior: The dot disappears and you can't check in Expected Behavior: You are able to check in Other bug scenario: If you have employee records in both Company A and Company B, you can check in. However, you can never check in for Company B as the default company is always selected in the server code opw-6392301 Forward-Port-Of: odoo/odoo#279675 Forward-Port-Of: odoo/odoo#278377
Previously, we did not prevent FRCTC from registering through Peppol, even though FRCTC is not supported in Peppol. French companies could attempt to register in Peppol with FRCTC, which caused many errors. task-6421930 Forward-Port-Of: odoo/odoo#278782
Original PR description
Previously, we did not prevent FRCTC from registering through Peppol, even though FRCTC is not supported in Peppol. French companies could attempt to register in Peppol with FRCTC, which caused many errors. task-6421930 Forward-Port-Of: odoo/odoo#278782
Before this commit, this test was failing non-deterministically on on some machines: ``` show banner for new message after thread was read from another device ``` With the following error: ``` Error: Failed to find 1 of ".o-mail-Thread-banner:has(:text('1 new message'))" (Timeout of 3 seconds). Found 0 instead. ``` This happens because while the message list has 20 messages, each message is 1-line long. That means on many monitors the bottom of conversation is visible and thus it c
Original PR description
Before this commit, this test was failing non-deterministically on on some machines:
```
show banner for new message after thread was read from another device
```
With the following error:
```
Error: Failed to find 1 of ".o-mail-Thread-banner:has(:text('1 new message'))" (Timeout of 3 seconds). Found 0 instead.
```
This happens because while the message list has 20 messages, each message is 1-line long. That means on many monitors the bottom of conversation is visible and thus it can mark as read automatically the conversation. When this happens the banner is removed, thus the last step would fail.
This commit fixes the issue by making each message body bigger, so that this is very unlikely to see the bottom of message list, therefore avoiding the auto-mark as read from reaching the bottom of conversation.
Forward-Port-Of: odoo/odoo#280103**Steps to reproduce:** 1. Install Accounting and l10n_be and switch to the Belgian company 2. In the settings, set the discount account (`708000`) on Customer Invoices under "Default Accounts" and enable analytic accounting 3. Go to [Accounting -> Configuration -> Analytic Accounts] and create 4 new accounts with "Project" plan (i.e 1,2,3,4) 4. Create a new invoice with two lines, each having 2 of the analytic accounts with 50% each. 5. Set the price to 1000 and a 10% discount for each lin
Original PR description
**Steps to reproduce:** 1. Install Accounting and l10n_be and switch to the Belgian company 2. In the settings, set the discount account (`708000`) on Customer Invoices under "Default Accounts" and…
**Steps to reproduce:** 1. Install Accounting and l10n_be and switch to the Belgian company 2. In the settings, set the discount account (`708000`) on Customer Invoices under "Default Accounts" and enable analytic accounting 3. Go to [Accounting -> Configuration -> Analytic Accounts] and create 4 new accounts with "Project" plan (i.e 1,2,3,4) 4. Create a new invoice with two lines, each having 2 of the analytic accounts with 50% each. 5. Set the price to 1000 and a 10% discount for each line then save. 6. Edit the second line and set the discount to 20%. 7. Open the Journal Items tab **Issue:** - When an invoice contains multiple lines with analytic distributions, changing the discount percentage on any line other than the first fails to correctly update the analytic distribution percentages on the corresponding discount journal items. - The analytic account distribution splits the percentage evenly across both accounts event if they are not split 50/50 **Why this happens:** - This occurred because `_compute_discount_allocation_needed` iterated over `self` to populate target changes. When only one line was modified, `self` contains that line only which is correctly updated with the new analytic distribution. Later in the execution in `_sync_dynamic_line`, particularly in https://github.com/odoo/odoo/blob/5a14360705a55f4d91edf39c936d7a5d8573044b/addons/account/models/account_move.py#L2263-L2274 The first line in `computed_needed` is what gets set in res, and subsequent lines only modify the field if it's monetary. So if the second invoice line is the one updated, it will never override the `analytic_distribution` with the updated values, leaving stale values in that field. - The code iterated directly over `line.analytic_distribution` dictionary keys (the account IDs) rather than its `.items()`. This caused it to ignore the individual percentage value splits (e.g. 60/40), accumulating the un-weighted full discount amount to each account ID. https://github.com/odoo/odoo/blob/5a14360705a55f4d91edf39c936d7a5d8573044b/addons/account/models/account_move_line.py#L1044-L1052 **Fix:** - Change the processing loop inside `_compute_discount_allocation_needed` from `self` to `self.move_id.line_ids` to calculate the correct `analytic_distribution` across all records. - Applying the factored weight ratio (`amount * (percentage / 100.0)`) to `distribution_totals` opw-6362084 Forward-Port-Of: odoo/odoo#279977 Forward-Port-Of: odoo/odoo#275070