Monday, September 28, 2026
10 changes · 18.0
Resolved issues and error corrections
This fixes an issue where employee details could be matched to the wrong person during large exports involving repeated employee records. The change keeps private and public employee data aligned, helping ensure exported HR-related information remains accurate.
Original PR description
Currently, `_copy_cache_from` is given matching private and public employee recordsets. Its goal is to take the public cache and copy the public record's value to the private cache using update_raw.…
Currently, `_copy_cache_from` is given matching private and public employee recordsets. Its goal is to take the public cache and copy the public record's value to the private cache using update_raw.
https://github.com/odoo/odoo/blob/4b94f02c0a60ec83a4c21e0c6bf31aa11c3f3d20/addons/hr/models/hr_employee.py#L256-L264
`update_raw` zips the recordset and values. This means the two lists need to be aligned; otherwise, `update_raw` will overwrite a private record's value with the value of a non-associated public record.
https://github.com/odoo/odoo/blob/4b94f02c0a60ec83a4c21e0c6bf31aa11c3f3d20/odoo/api.py#L1080-L1101
**Steps to reproduce:**
1. Have a list of records with a related employee field
Example: Departments each with a manager (employee)
2. Have the record size be over the export batch size (1000)
3. Ensure some managers from the first batch are included in the second
4. Export records
Issue:
After the first batch, the employee data is cached. When the second
batch is run, some employees are cached (seen managers), but some
are not. This causes the misalignment in _copy_cache_from.
**Fix:**
Fetch the values while keeping track of which private record each one is associated with.
opw-6289990
Forward-Port-Of: odoo/odoo#279482Credit notes created from invoice reversals now keep the same currency exchange rate as the original invoice. This prevents unnecessary exchange difference entries and keeps accounting results aligned when correcting or reversing foreign-currency invoices.
Original PR description
Adjusting move reversal wizard to pass origin move currency rate to the credit note being created, avoiding creating exchange differences. task-6586992 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Field Service reports now include custom fields added to worksheet templates through Studio. This prevents printed PDF reports from missing important worksheet information after users customize their forms.
Original PR description
### Steps to Reproduce: 1. Create a new Worksheet Template in the Field Service module 2. Add a new field using Design Template button 3. Create a new task and assign the new template and add in…
### Steps to Reproduce: 1. Create a new Worksheet Template in the Field Service module 2. Add a new field using Design Template button 3. Create a new task and assign the new template and add in necessary info to save 4. Fill out the worksheet from the smart button 5. Go back to the task and print out the Field Service Report 6. Notice that only the comments show up, the new field(s) is not visible ### Issue: When adding new fields to a Field Service Worksheet Template using Studio, the fields appear correctly on the worksheet form but fail to render on the printed PDF Field Service Report. This happens because the QWeb generator strictly searches for direct child fields and ignores fields that are wrapped in structural layout tags by Studio. Modifying the form view in Studio does not natively trigger a regeneration of the static QWeb report, which makes the PDF out of sync with the database. ### Solution: Updated the report generation logic to use an XPath search (`.//field[not(ancestor::field)]`), which ensures that nested Studio fields are found and parsed. Additionally, added a listener to `ir.ui.view` that watches for layout modifications on worksheet templates. Now, whenever a user updates a worksheet form in Studio, the system automatically forces the PDF layout to rebuild and include the newly added fields. opw-6484767 Before: [Field Service Report - TEST TASK - Acme Corporation.pdf](https://github.com/user-attachments/files/31525820/Field.Service.Report.-.TEST.TASK.-.Acme.Corporation.pdf) After: [Field Service Report - testtest - Acme Corporation.pdf](https://github.com/user-attachments/files/31525821/Field.Service.Report.-.testtest.-.Acme.Corporation.pdf)
Brazilian electronic invoice returns now reference the original invoice on each item instead of only at the document level. This keeps returns compliant with SEFAZ NT 2025.002 and prevents Avalara rejections, while preserving the required behavior for debit notes and service documents.
Original PR description
SEFAZ NT 2025.002 requires a return (finNFe 4) to reference the original NF-e per item. Avalara enforces it from engine 26.7.4, rejecting returns with "Documento referenced is missing for line N". Avalara confirmed the header reference must be dropped too. Its documentCode form comes from the calculate payload, so it needs an explicit pop. Debit notes and services keep it because SEFAZ rejects a complementary NF-e without one. task-6519802
Completed manufacturing orders now keep the labor cost that was recorded when the work was finished, even if an employee's hourly rate changes later. This prevents past manufacturing reports from changing unexpectedly and keeps cost reporting consistent with booked values.
Original PR description
Steps to reproduce
1. Set an employee's Hourly Cost to 100.
2. Create and complete an MO with 1 hour of that employee's time logged on its work order.
3. Open the MO Overview and note the Real Cost (100 of labour).
4. Change the employee's Hourly Cost to 200.
5. Reopen the MO Overview.
Expected: Real Cost stays 100, booked when the MO was completed.
Actual: the labour part becomes 200, following the new rate.
Issue
---
`employee_cost` on `mrp.workcenter.productivity` is stored but computed with `@api.depends('employee_id.hourly_cost')`, so raising an employee's `hourly_cost` recomputes it on past time logs. The MO Overview sums `time_ids.total_cost` live through `_compute_current_operation_cost`, so a done MO's cost drifts from what was booked at completion. `_compute_employee_cost` now skips logs whose MO is `done`, keeping their booked rate while in-progress MOs still track edits.
opw-6567219Peruvian credit and debit notes in foreign currency now use the exchange rate from the original invoice they modify, instead of the note's own date. This keeps PEN tax amounts aligned with SUNAT rules and reduces incorrect exchange differences and reporting mismatches.
Original PR description
Description of the issue/feature this PR addresses: In Peru, foreign currency operations are converted to PEN with the exchange rate of the date on which the IGV obligation arises (article 5, numeral…
Description of the issue/feature this PR addresses: In Peru, foreign currency operations are converted to PEN with the exchange rate of the date on which the IGV obligation arises (article 5, numeral 17 of the IGV Law Regulation). SUNAT considers that a credit or debit note only changes the amount of the operation it modifies, not the date on which that obligation arose, so the note has to be converted with the exchange rate of the modified document (Oficio N.° 024-2000-K00000 on a credit note, Informe N.° 118-2016-SUNAT/5D0000 on a debit note subject to the SPOT system). The SIRE proposals apply the same rule (RVIE: note 3 of Annex 2 of RS N.° 112-2021/SUNAT, as amended by RS N.° 000138-2023/SUNAT; RCE: notes 1 and 4 of Annex 8 of RS N.° 040-2022/SUNAT). Legal basis (official SUNAT sources): - IGV Law Regulation, article 5, numeral 17: https://www.sunat.gob.pe/legislacion/igv/regla/cap4.htm - Oficio N.° 024-2000-K00000: https://www.sunat.gob.pe/legislacion/oficios/2000/oficios/o0242000.htm - Informe N.° 118-2016-SUNAT/5D0000: https://www.sunat.gob.pe/legislacion/oficios/2016/informe-oficios/i118-2016.pdf - RVIE, Annex 2, note 3 (RS N.° 000138-2023/SUNAT): https://www.sunat.gob.pe/legislacion/superin/2023/anexoII-000138-2023.pdf - RCE, Annex 8, notes 1 and 4 (RS N.° 040-2022/SUNAT, p. 28): https://www.sunat.gob.pe/legislacion/superin/2022/anexo-040-2022.pdf Current behavior before PR: Credit and debit notes currently use the rate of their own invoice date, so: - their PEN amounts (base and IGV) differ from the ones of the modified invoice; - an exchange difference entry is created when they are reconciled with the modified invoice; - the rate reported in the PLE/SIRE files differs from the SUNAT proposal. Steps to reproduce: - PE company with USD rates of 4.00 PEN on 2024-01-01 and 5.00 PEN on 2024-02-01 - Post a USD invoice of 1,000.00 dated 2024-01-15 - Add a credit note dated 2024-02-15 - The credit note is converted at 5.00 (5,000.00 PEN) instead of 4.00 (4,000.00 PEN) Desired behavior after PR is merged: For PE companies, the date used to compute the currency rate of a credit or debit note linked to the document it modifies is now the rate date of that document, as done for l10n_cz and l10n_hu_edi in 063dba210a39. As that date is taken recursively, a note of a note keeps the rate of the first document, so that a note and its reversal share the same rate. The link fields are added as dependencies of the rate computations, so that the rate is recomputed when a note is linked after its creation. The rate date is adapted instead of copying the rate of the modified document because the rule refers to the rate published on that date, and because the expected rate then stays equal to the invoice rate, so the rate is not displayed as manually modified. 17.0 counterpart: odoo/odoo#288654. In 17.0 the currency rate is computed on each invoice line, so the fix is done on `account.move.line._get_rate_date` (as in d5a772458d4e) and forward-port is disabled there. This PR follows the normal forward-port. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Users expanding a record from views such as Gantt now see the intended form instead of a generic default form. This helps portal and shared project users land on the right screen, reducing confusion and keeping the workflow consistent.
Original PR description
When a user expands the form view (e.g., from the Gantt view),
a default low-priority form view is opened because the
specific form view ID is not passed during dialog initialization.
This commit introduces the expandedFormRef props to FormViewDialog,
allowing users to specify the exact form view to be used when expanding.
Use case:
In project task sharing, when a portal user expands a task from
the Gantt view, we want to open the project sharing form view
instead of the default backend view.By passing the expandedFormRef
(form view ID) while opening the dialog, we ensure the correct view
is used.
this.dialogService.add(
FormViewDialog,
{
title,
resModel,
...
context: {
expandedFormRef: formViewId,
}
}
);
backport-https://github.com/odoo/odoo/pull/199912/changes/4f71fbbd26b428e57943d974e8441bef295cdef1
task-6356431This fix prevents cards from jumping away from the cursor when users drag them near the edge of wide kanban-style views in right-to-left languages. It improves usability for teams working in RTL interfaces by keeping dragged items aligned with the user's pointer.
Original PR description
Before this commit, dragging an element (e.g. a kanban card) close to the edge of a horizontally overflowing container in a RTL language made it jump away from the cursor instead of following it. This is because `updateRects()` computes the horizontal bounds used to clamp the dragged element's position by assuming overflowing content always extends to the *right* of the container. (`containerRect.left + container.scrollWidth`). This holds in LTR, but in RTL a flex row starts from the right, so overflowing content extends to the left of the container's box instead. The container's own computed direction is now used to anchor the overflow calculation on the correct edge. opw-6488992
This fixes an issue where attachments with apostrophes or other special characters in the filename could download as a text file containing a link instead of the real file. Users can now download these attachments normally, reducing confusion and failed file access in everyday workflows.
Original PR description
**Description of the issue/feature this PR addresses:** When downloading an attachment with an apostrophe in its name (e.g., file'name.zip), the system incorrectly downloads a text file containing…
**Description of the issue/feature this PR addresses:** When downloading an attachment with an apostrophe in its name (e.g., file'name.zip), the system incorrectly downloads a text file containing the URL instead of the actual file. This occurs because of the current url validation check: `if (anchor.href.indexOf(url) !== -1)`. When the raw URL is assigned to the anchor's href attribute, the browser automatically normalizes special characters (converting the literal ' to %27). The strict string comparison then fails, causing the script to skip the XHR request and fall back to generating a Blob from the raw URL string. This commit resolves the issue by removing the overly strict indexOf check. Based on the function's description, if it is called with only one argument (no filename and no mimetype), the system already expects that argument to be a URL. By removing the check, we proceed directly to the XHR request. If the payload is a valid URL (even with normalized characters), the download succeeds. If raw contents are passed by mistake, the system will now fail loudly with an XHR error that is easy to debug, rather than silently downloading a text file named after its own contents. **Steps to reproduce:** - Discuss > choose any chat and upload any file containing an apostrophe > attempt to download the file > observe incorrect text file containing the URL string **Current behavior before PR:** - Downloading attachments with special characters such as apostrophes results in a text file containing the URL instead of the actual file **Desired behavior after PR is merged:** - Downloading attachments with special characters such as apostrophes results in the actual file opw-6580287
Invoices sent through Peppol are no longer blocked when a line has a quantity of zero. This prevents valid invoices from being incorrectly rejected as missing quantity information, helping businesses send special-case or adjustment invoices without manual workarounds.
Original PR description
Fixes #289575. ### Impacted versions 18.0 and 19.0. The constraint arrived with 081221a4cc2ff72f16be0e1d073112b938de0f19 ("[FIX] account_edi_ubl_cii, account_peppol: make PINT use UBL export",…
Fixes #289575.
### Impacted versions
18.0 and 19.0. The constraint arrived with 081221a4cc2ff72f16be0e1d073112b938de0f19 ("[FIX] account_edi_ubl_cii, account_peppol: make PINT use UBL export", 2026-08-20).
master is not affected: the IBR constraint block was dropped from `account_edi_ubl_pint.py` there, so nothing checks the quantity.
### Steps to reproduce
1. A company with Peppol enabled, and a customer with a Peppol endpoint.
2. Create a customer invoice and set the **quantity to 0** on one line, leaving its unit price set.
3. Confirm, then **Print & Send** over Peppol.
### Current behavior
Sending is refused with `Line 1: Invoiced quantity is missing [IBR-022]`, although the quantity is there: `cbc:InvoicedQuantity` is built for every line and renders as `0.0`.
`account_edi_ubl_pint.py` read
```python
qty_node = line['cbc:InvoicedQuantity'] if is_invoice else line.get('cbc:CreditedQuantity', {})
if qty_node.get('_text') in [None, False]:
```
and `_text` holds the raw float taken from `base_line['quantity']` in `_ubl_add_line_invoiced_quantity_node`. Since `0.0 == False` in python, `0.0 in [None, False]` is `True`, so a present quantity of 0 is treated as missing.
`account.edi.xml.ubl_bis3` inherits `account.edi.ubl_pint_eu`, so this refuses plain Peppol BIS Billing 3.0 exports too, not only PINT.
### Expected behavior
The line exports and sends. A quantity of 0 is a value, and EN16931 BR-22 asks for the invoiced quantity to be present, not to be non-zero.
### About the fix
Absence is tested by identity, so 0 passes while a missing or `False` value is still reported:
| `_text` | before | after |
|---|---|---|
| `0.0` | reported missing | accepted |
| `0` | reported missing | accepted |
| `1.0` | accepted | accepted |
| `None` | reported missing | reported missing |
| `False` | reported missing | reported missing |
`isinstance(qty_text, (int, float))` would have been the shorter test but `isinstance(False, int)` is `True` in python, so `False` would no longer be caught.
The neighbouring constraints in that method do not share the problem. Nine of them read string values, where falsiness is the intended test, and the only other numeric one checks `isinstance(line['cbc:LineExtensionAmount']['_text'], FloatFmt)`, which no value of 0 can fail. `in [None, False]` occurs nowhere else under `addons/`.
### Test
`TestUblExportBis3BE.test_ubl_line_with_zero_quantity`, next to the existing `test_ubl_line_without_product_name`, which it follows. It asserts only on the quantity constraint, so the other constraints a minimal test invoice raises do not mask it.
Verified both ways on 18.0:
- without the fix: `AssertionError: ['Line 1: Invoiced quantity is missing [IBR-022].'] is not false`
- with the fix: `0 failed, 0 error(s) of 1 tests`
### Credit
Reported and diagnosed by @Robrechtc in #289575, including the cause and the suggested fix.