Thursday, July 2, 2026
17 changes · master
Resolved issues and error corrections
When a sign template is duplicated, its roles are now copied too instead of being shared with the original template. This prevents changes made on one template from unexpectedly affecting another, keeping each document setup independent.
Original PR description
When duplicating a sign template, its sign items were copied but their `responsible_id` was kept as a reference to the same `sign.item.role` records. As a result, editing a role on one template (e.g. assigning a partner through `assign_to`) leaked to the other template sharing it. Copy the role when copying a sign item so each template owns its own roles. task-6288951 Forward-Port-Of: odoo/enterprise#121411 Forward-Port-Of: odoo/enterprise#119864
This update prevents an error that could occur when generating the GSTR-1 Excel report in a multi-company environment, especially when only one company is selected for export. It ensures the report completes successfully instead of failing during Excel generation, making tax reporting more reliable for users with branch companies.
Original PR description
Steps to reproduce: - Install `l10n_in_reports` module(Indian Localisation) - Create a branch in `IN Company` > Select both - Create separate invoices for each company - Created the GSTR-1 report for…
Steps to reproduce:
- Install `l10n_in_reports` module(Indian Localisation)
- Create a branch in `IN Company` > Select both
- Create separate invoices for each company
- Created the GSTR-1 report for both company
- While generating Excel, select only main company
Traceback:
```py
File "/home/odoo/src/enterprise/19.0/l10n_in_reports/models/account_return.py", line 2537, in action_generate_gstr1_xlsx
gstr1_json = self._get_l10n_in_gstr1_json()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/19.0/l10n_in_reports/models/account_return.py", line 1071, in _get_l10n_in_gstr1_json
'b2cs': _get_b2cs_json(AccountMoveLine.search(self._get_section_domain('b2cs'))),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/19.0/l10n_in_reports/models/account_return.py", line 672, in _get_b2cs_json
for line, line_tax_details in tax_details.items():
^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'items'
```
Cause:
This issue occurs because, while generating the GSTR-1 Excel report for a particular month, [journal_items] contains account moves from both companies. This happens because the [domain] fetches records for both companies, resulting in move [lines] from both companies being included.
However, while generating the Excel report, only one company is selected. As a result, [tax_details_by_move] does not contain the move data for the branch company, which returns None, causing the error to be raised.
Solution:
Pass an empty `{}` for `tax_details` when only a single company is selected.
[journal_items]: https://github.com/odoo/enterprise/blob/3c3641d9f0753c1ca62285667c4a2786c15e2444/l10n_in_reports/models/account_return.py#L877
[domain]: https://github.com/odoo/enterprise/blob/770ffaac14bfcd2c54a7ce6aca27e0010e7884d4/l10n_in_reports/models/account_return.py#L1387-L1393
[lines]:
https://github.com/odoo/enterprise/blob/3c3641d9f0753c1ca62285667c4a2786c15e2444/l10n_in_reports/models/account_return.py#L1074
[tax_details_by_move]:
https://github.com/odoo/enterprise/blob/3c3641d9f0753c1ca62285667c4a2786c15e2444/l10n_in_reports/models/account_return.py#L880
opw-6242824
Forward-Port-Of: odoo/enterprise#118614This update corrects how the current year’s earnings account is classified and simplifies a related balance sheet line in the Luxembourg reports. It helps ensure the financial statements show the result brought forward more accurately and with less complexity.
Original PR description
This commit addresses the account type for the current year earnings and simplifies the calculation for the "Result brought forward" line in the Luxembourg balance sheet reports.
Modifications:
* Changed the account type of account 142 ("Result for the financial year") from `equity_unaffected` to standard `equity`.
* Simplified the formula for the Balance Sheet line "Profit or loss brought forward" (codes `LU_BS_319` and `LU_BSABR_319`).
* The new formula simply targets the `14` accounts while explicitly excluding `142`.
Community PR: odoo/odoo#272362
Ticket [link](https://www.odoo.com/odoo/project.task/6059571)
opw-6059571
Forward-Port-Of: odoo/enterprise#122097
Forward-Port-Of: odoo/enterprise#121891This update fixes an error that could prevent PDF generation for certain Guatemala vendor bills, especially FESP documents with withholding taxes. Users can now download the bill PDF successfully after sending it to SAT, avoiding interruptions in the invoicing workflow.
Original PR description
**Steps to reproduce:** - Install the `l10n_gt_edi` module and switch to a `GT Company`. - Create a new vendor bill with: - Vendor: GT Company - GT Document Type: `FESP` - Add taxes `VAT Withholding…
**Steps to reproduce:** - Install the `l10n_gt_edi` module and switch to a `GT Company`. - Create a new vendor bill with: - Vendor: GT Company - GT Document Type: `FESP` - Add taxes `VAT Withholding 12%` and `ISR Withholding 5%` in Invoice lines. - `Confirm` the bill and `Send to SAT`. - From the gear icon, click `Download` > `PDF`. **Error1:** `KeyError: 'gran_total'` **Error2:** `KeyError: 'retencion_grand_total'` **Root Cause:** In commit [1], the code at [2] missed calling `_l10n_gt_edi_add_base_values()` before `_l10n_gt_edi_add_withholding_values()`. However, `_l10n_gt_edi_add_withholding_values()` uses the `gran_total` value, which is initialized by `_l10n_gt_edi_add_base_values()`, resulting in a `KeyError`. Additionally, the report template at [3] references `retencion_grand_total` instead of the correct key `retencion_gran_total`, causing another `KeyError`. **Fix:** This commit prevents errors and ensures users can successfully download the PDF by applying a fix similar to [4], [1]: https://github.com/odoo/enterprise/commit/44afd19e4ed0827e343af0e584c81e579935c9e8 [2]: https://github.com/odoo/enterprise/blob/9846b337cfe1876017c7c2ce3041569d7a2ac03f/l10n_gt_edi/models/account_move.py#L305-L328 [3]: https://github.com/odoo/enterprise/blob/9846b337cfe1876017c7c2ce3041569d7a2ac03f/l10n_gt_edi/views/report_invoice.xml#L72 [4]: https://github.com/odoo/enterprise/blob/9846b337cfe1876017c7c2ce3041569d7a2ac03f/l10n_gt_edi/models/account_move.py#L790-L800 opw-6323049 Forward-Port-Of: odoo/enterprise#122398 Forward-Port-Of: odoo/enterprise#122030
This update ensures invoices sent through the Guatemalan EDI service are encoded correctly, so names and product descriptions with accents or other special characters are preserved. It reduces failed submissions and prevents certified documents from coming back with missing or corrupted text.
Original PR description
**Steps to reproduce:** * Install the **l10n_gt_edi** module. * Configure a Guatemalan company with valid Infile credentials in the settings. * Create a product or customer with special characters…
**Steps to reproduce:**
* Install the **l10n_gt_edi** module.
* Configure a Guatemalan company with valid Infile credentials in the settings.
* Create a product or customer with special characters (e.g. `ñ`, `á`, `é`) in their name.
* Create a customer invoice containing this product/customer.
* Confirm the invoice to trigger the EDI send to the SAT (Infile).
**Observed behavior:**
* Infile intermittently rejects the invoice due to validation errors, or accepts it but the resulting certified XML has truncated or malformed text exactly where the special characters were located.
**Cause:**
* Odoo uses the `requests.post()` library to send the XML payload to Infile. By default, `requests` encodes string payloads using `latin-1` unless told otherwise.
* Because the request was missing the explicit `Content-Type: application/xml` header and the XML string was not explicitly encoded to `utf-8` before sending, Infile parsed the payload using an incorrect encoding. This caused it to drop or misinterpret special characters, leading to validation failures or corrupted XML content.
**Fix:**
* Explicitly include the `'Content-Type': 'application/xml'` header in the request to Infile.
* Explicitly encode the `xml_data` payload to `utf-8` (`xml_data.encode('utf-8')`) before passing it to `requests.post()` to guarantee the correct encoding is sent over the wire.
opw-6315654
Forward-Port-Of: odoo/enterprise#121729Selling combo products in the Kenyan Point of Sale will no longer be blocked by an eTIMS registration warning when only the items inside the combo are registered. The system now correctly ignores the combo wrapper item for eTIMS checks, so checkout can proceed normally and receipts are generated without errors.
Original PR description
Steps to reproduce ------------------ 1. Install l10n_ke_edi_oscu_pos. 2. Select the Kenyan company. 3. Enable eTIMS on the PoS. 4. Register the products inside a combo, but not the combo itself. 5.…
Steps to reproduce ------------------ 1. Install l10n_ke_edi_oscu_pos. 2. Select the Kenyan company. 3. Enable eTIMS on the PoS. 4. Register the products inside a combo, but not the combo itself. 5. Sell the combo in the PoS. Observation ----------- We see a warning that the combo must be registered to eTIMS, and the order can't be validated. What's happening ---------------- In the PoS a combo adds a 0 price parent line for the combo product, but the combo is not a real item to send to eTIMS, only the products inside it are, and (as per step 4) the combo is not registered. `checkEtimsFields` sees the combo as not registered, so it raises the warning in `showUnregisteredProductsWarning` and blocks the payment in `validateOrder`. Fix --- In the backend, we skip sending the parent combo line to eTIMS, and on the frontend, we make the combo parent line not need eTIMS registration, so the warning and the block don't apply to it. opw-6253306 Forward-Port-Of: odoo/enterprise#122179 Forward-Port-Of: odoo/enterprise#119362
This change makes the “Undo reconciliation” action bypass extra checks so the reconciliation line can be reset correctly. It helps users reverse a reconciliation without being blocked by validation steps that are not needed in this case.
Original PR description
When undoing the reconciliation from the "undo reconciliation" button. We want to bypass all the checks to be able to reset the line. no task id Forward-Port-Of: odoo/enterprise#122166 Forward-Port-Of: odoo/enterprise#121611
This fix ensures that documents signed through eMSigner are saved with the final signed PDF, instead of sometimes keeping the original uploaded file. It matters because users will now receive the correct completed document after signing, improving trust and avoiding rework.
Original PR description
Version: - saas-19.3 Steps to reproduce: - Create a sign request using the eMSigner authentication method. - Complete the signing process. - Download the completed document. Issue: - Users received the original uploaded PDF instead of the signed PDF after completing the signing process through eMSigner. Cause: - After the BinaryValue migration, the completed document was initialized with the original document (document.raw) and only replaced with the eMSigner response for large compressed files. As a result, non-compressed responses stored the original document instead of the signed PDF returned by eMSigner. Fix: - Always use the signed document returned by eMSigner (decrypted_data) to create the completed document. Decode the base64 response and, for large files, decompress it before storing it as binary content. task-6329040 Forward-Port-Of: odoo/enterprise#121628
When a new company is created, its main employee folder will now be placed in the Company root instead of being created inside a personal My Drive area. This makes the folder structure clearer and ensures employee folders are organized where users expect them.
Original PR description
Steps to reproduce =================== - Install documents_hr. - Log in with admin. - Create a new company `Test`. - Go to Document and choose the new company (top right). - Go to `My Drive`: a folder named `Employees - Test` has been created. This new folder should be created in the `Company` root instead of the `My Drive,` which will hold all the employee folders. Technical =========== When the main employee folder is created via `_generate_employee_documents_main_folders` `owner_id` falls back to the current user, which leads to computing the `user_folder_id` as `My drive,` and so that's why the newly created folder starts appearing there instead of the `Company` root. This PR addresses the issue and sets the `owner_id` to False, which leads to show the main employee folder in the company root. Task-6267352 Forward-Port-Of: odoo/enterprise#120677
This change prevents an access error that could block delivery validation for users with limited Sales access. It ensures the system can safely check the subscription status needed for the delivery process, without exposing unrelated sales documents.
Original PR description
## **Issue:** When validating a delivery, users with Sales: Own Documents Only and Inventory Administrator access rights can encounter an access error if the delivery belongs to a sale order owned by…
## **Issue:** When validating a delivery, users with Sales: Own Documents Only and Inventory Administrator access rights can encounter an access error if the delivery belongs to a sale order owned by another salesperson. ## **Steps to reproduce:** - Install sale_subscription_stock. - Create a user with Sales: Own Documents Only and Inventory Administrator access rights. - Create a sale order as another user. - Validate the delivery with the restricted user. ## **Solution:** During _action_done(), [This line](https://github.com/odoo/enterprise/blob/19.0/sale_subscription_stock/models/stock_picking.py#L45) is checking subscription_state. Since the user does not have read access to the sale order, reading this field raises an access error and prevents the delivery from being validated. As the method only needs to read the subscription state, access the field with sudo() to avoid the unnecessary access error while preserving the existing business logic. Runbot Video : [Video](https://drive.google.com/file/d/1d7U2jTCxaaVk2YJcy3bi2SlYuT-yXMsu/view?usp=drive_link) OPW - 6295712 Forward-Port-Of: odoo/enterprise#122420
This fix ensures planning hours are calculated correctly when a slot is moved in and out of an employee’s scheduled working time. It prevents incorrect break-time and allocated-hours values from appearing after repeated time changes, keeping planning reports and service scheduling accurate.
Original PR description
Issue: ---------------------------------------- When changing multiple times the hours of a slot to include out-of-schedule time. Steps to reproduce: ---------------------------------------- - Have…
Issue: ---------------------------------------- When changing multiple times the hours of a slot to include out-of-schedule time. Steps to reproduce: ---------------------------------------- - Have planning_field_service installed - Have an employee with a schedule from 7am to 3pm - In planning view, create a new slot for this employee from 7am to 3pm (8h) - Change the starting hour to 6am (8h + 1h of break time) - Change it back to 7am - The slot shows 7h07 of allocated hours and 53 minutes of break time Cause: ---------------------------------------- Since 8ca9faabfdf7d15883ba52da47bd8c562cf601de the compute of `allocated_percentage` is overriden in `planning_field_service`. The new compute uses `break_time` to recompute `allocated_percentage`. `break_time` is the not work time over the whole duration of the slot, including hours out of schedule. But the definition of `allocated_percentage` in `planning` is: the percentage of slot hours in schedule which are actually worked. So when changing the start to 6am `allocated_percentage` is still supposed ot be 100% because the employee is working 100% of the hours he is supposed to work considering its schedule. With the actual code `allocated_percentage` is actually computed as 8/9 = 0.88888... because it will take into account the hours out of schedule. As `allocated_percentage` is not recomputed if `allocated_hours` or `break_time` aren't modified by the user. It is then used [here](https://github.com/odoo/enterprise/blob/ae5008bdaf1b87269083f82280c7df44390129ff/planning/models/planning_slot.py#L2865-L2867) to compute the allocated_hours and the number of hours in schedule is divided base onthe percentage. Solution: ---------------------------------------- We only consider the hours in schedule to recompute `allocated_percentage`. `allocated_percentage` was used in `_onchange_break_time()` to get the previous ratio and calculate the allocated hours from which we deduct the break time. We cannot do this now so we also need to compute the working hours. ----------------------------------------- # [FIX] planning_field_service: handle input of negative break_time Issue: ---------------------------------------- When inputting negative break_time for a slot, it's possible to get a traceback. Steps to reproduce: ---------------------------------------- - Have planning_field_service installed - Have an employee with a schedule from 7am to 3pm - In planning view, create a new slot for this employee from 7am to 3pm (8h) - Input 9h of break time - Input -1h of break time - Traceback Cause: ---------------------------------------- When `slot.allocated_hours` is 0 and we input a negative value in `break_time`, the code in `_onchange_break_time()` will give `allocated_hours` the positive value of `break_time` making them opposite. Then in [`_compute_allocated_percentage()`](https://github.com/odoo/enterprise/blob/188dcc5078be7c7fee1a52f86505144d3b6309cf/planning_field_service/models/planning_slot.py#L98) we divide by their sum, which equals 0. Solution: ---------------------------------------- We compute the divider part and check if it's zero in `_compute_allocated_percentage()`. Also add a `max()` in `_onchange_break_time()` to convert the negative break time in allocated hours and resets `break_time` to zero. This ensures the same behavior as inputting negative values in `allocated_hours`. opw-6273559 Forward-Port-Of: odoo/enterprise#122279 Forward-Port-Of: odoo/enterprise#121168
The Planning / Timesheets Analysis report now correctly includes work slots scheduled on calendars that use duration-based attendances. This fixes missing entries in reporting so businesses get a complete view of planned time and timesheet analysis.
Original PR description
Issue: ---------------------------------------- The Planning / Timesheets Analysis report doesn't include most of the planning slots if they have a calendar based on duration. Steps to reproduce:…
Issue: ---------------------------------------- The Planning / Timesheets Analysis report doesn't include most of the planning slots if they have a calendar based on duration. Steps to reproduce: ---------------------------------------- - Have a 1 day planning slot (8h-17h) for an employee with a calendar based on duration. - Planning > Reporting > Planning / Timesheets Analysis - The slot is not included in the report Cause: ---------------------------------------- The update of how calendars work in saas-19.2 included the `duration_based` option but the query was not adapted. It still checks if the slot start time is lower than the calendar end time on that day: ```sql F.start_datetime < (d.date::date + (A.hour_to || ' hour')::interval) AND F.end_datetime > (d.date::date + (A.hour_from || ' hour')::interval) ``` But for duration-based calendars `hour_from` and `hour_to` are stored as `0.0`. So `F.start_datetime < (d.date::date + (A.hour_to || ' hour')::interval)` evaluates to False. Solution: ---------------------------------------- If the calendar has `duration_based` to True we only check the date. opw-6217643 Forward-Port-Of: odoo/enterprise#122173 Forward-Port-Of: odoo/enterprise#118133
This change prevents an error when a user opens a shift that has overlapping conflicts they are not allowed to see. For users without Planning Administrator rights, conflict details are no longer computed, which avoids the access issue and keeps shift viewing smooth.
Original PR description
Steps to reproduce: - Create a shift assigned to resources A and B - Create another overlapping shift assigned only to resource B - Login as resource A with internal user access only - Open the first shift Issue: An access error is raised when opening the shift. Cause: The conflict computation fetches overlapping shifts using SQL, which can return shifts that are not accessible to the current user. Solution: Return empty conflict values for users without Planning Administrator access, as conflict warnings are only available to planning managers. task-6313628 Forward-Port-Of: odoo/enterprise#122252 Forward-Port-Of: odoo/enterprise#121465
When users work across multiple companies, the Helpdesk ticket quick-create form will now only show customers from the appropriate company. This prevents selecting the wrong customer and helps teams create tickets more accurately.
Original PR description
Steps to reproduce: - Create two companies (Company A and Company B) - Create one partner in each company - Enable both companies for the user - Open Helpdesk and go to the tickets Kanban view for a Company A team. - In the quick create form, the customer dropdown shows customers from Company B Issue: - Customers from other companies are visible in the customer field, Cause: - The partner_id field in the quick create view had no domain, so it displayed partners from all allowed companies. Solution: - Added a domain on partner_id in the ticket quick create form view. task-4971466 Forward-Port-Of: odoo/enterprise#122358 Forward-Port-Of: odoo/enterprise#121944
Shared audio files can no longer be previewed directly in the browser. This avoids playback issues in Chrome and keeps shared document links working consistently by directing users to download the file instead.
Original PR description
**Steps to reproduce:** - Install Documents app - Upload a sound file (mp3 for example, but the behavior is the same for other formats) - Share the document and copy the link - Log out - Paste the…
**Steps to reproduce:**
- Install Documents app
- Upload a sound file (mp3 for example, but the
behavior is the same for other formats)
- Share the document and copy the link
- Log out
- Paste the link
- Click on preview button
- Media is working fine on Firefox
- Media won't read on Chrome
```
Loading media from '' violates the following Content Security Policy directive: "default-src 'none'".
Note that 'media-src' was not explicitly set, so 'default-src' is used as a fallback.
The action has been blocked.
```
**Issue:**
Since [1] default CSP Headers are too strict for Chrome default media rendering, which breaks the file preview (and force user download).
This only impacts Chrome as they seem to render generate `<video><source>` elements to render the file which triggers a secondary request and fails due to the CSP constraint.
**Fix:**
Could re-apply the header fix of 17.4 (see [2]), but it seems better to block the preview of audio files as well by default (to match how we manage videos).
(Note: we don't want to be used as a media streaming platform)
[1] (set csp to none by default) https://github.com/odoo/odoo/commit/64beb80205dffe4b432c8b5813a3271b073fa85e
[2] (similar issue which was not fixed in 18.0+) https://github.com/odoo/enterprise/commit/a7af78eeb2b9763045a5bda8714172f5e7402df8
[3] (mp4 preview removed) https://github.com/odoo/enterprise/commit/ea88cf7c6d5f077b60fb59347659507fded0e2fe
opw-6235043
Forward-Port-Of: odoo/enterprise#119644This update corrects the reimbursement rate used for bicycle travel in Belgian payroll. It ensures employees are paid the intended amount instead of being capped too high, which avoids overpayments and makes payslips accurate.
Original PR description
Steps to reproduce: 1. Employee Setup: CP200, Worker 495, Bike 18 km, Car 60 km. 2. Action: Compute 05/2026 payslip (21 worked days) and close pay. Got 226.80€ (stuck at 10.80€/day cap) -> Expected 204.12€ (18 km × 2 × 21 days × 0.27€). Solution: change rule parameter values from 0.36 to 0.27 Task: 6334739
This update clarifies the payroll correction process by renaming the 'Revert' button to 'Correct' and linking it to a dedicated wizard. This ensures users are guided through the necessary steps for correcting payroll data, promoting accuracy and consistency within the payroll validation workflow.
Original PR description
Renaming the button from 'Revert' to 'Correct'. Furthermore, triggering the existing hr.payslip.correction.wizard instead of a direct state reversion ensures that users are guided through the proper corrective parameters, maintaining consistency across the payroll validation workflow. task-6310911