Friday, August 28, 2026
19 changes · 18.0
Resolved issues and error corrections
The Assets list view now avoids a repeated reload loop when an asset contains outdated or invalid analytic distribution data. This keeps the asset list accessible for users, even if some underlying analytic account references need cleanup.
Original PR description
Issue - If there are any account.asset records with analytic distributions with accounts that do not exist, it causes a recursive traceback when opening the list view of the `account.asset` model. The issue stems from `jsonToData` attempting to save the distributions json via the `save` call, where one (or multiple) accounts are non existent, which in turn runs `jsonToData` after refetching via the `load` call - overwriting `record.data` with the original, still-corrupt JSON. This creates a loop with no exit condition. Solution - In the Assets list every row is readonly, so `save()` is never reached, so `root.load()` never fires, so there is no reload to re-read the corrupt JSON. Makes the list accessible, even though the JSON values for the `analytic_distribution` are invalid. opw-6500446
This fix prevents restaurant orders from losing their order lines when the same table is edited across multiple POS devices. It protects sales records from being saved with payments and totals but no products, improving reliability in multi-device restaurant workflows.
Original PR description
Steps to reproduce: - Restaurant config with two POS devices on the same session - Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids) -…
Steps to reproduce:
- Restaurant config with two POS devices on the same session
- Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids)
- Device A: remove both lines, without syncing
- Device B: touch the same order, so device A re-reads it from the server through the synchronisation websocket
- Device A: pay and validate the order
Issue:
The order is saved as paid, with its total and its payment, but without any orderline. The lines are deleted on the server: odoo.models.unlink: deleted pos.order.line records with IDs: [...]
Cause:
Removing an orderline queues an unlink command in
models.commands['pos.order'].unlink['lines_<order id>'] (delete_ in related_models.js). That command is only discarded by clearCommands(), which syncAllOrders() calls after a successful sync, so it stays pending in between.
Any read of the order in that window puts the line back: a deleted record counts as missing in missingRecursive(), so it is fetched again and loadData() re-creates it with its server id.
serialize() then emits both the update of the live line and the still pending unlink, and sync_from_ui writes
lines: [[1, id, {...}], [3, id]]. The ORM applies commands in order, and pos.order.line.order_id is ondelete='cascade', so Command.UNLINK deletes the line right after writing it.
Fix:
Skip a removal command when the record is still linked to the parent at serialization time. A record cannot be both linked and removed in the same payload, so the pending command is stale and dropping it keeps the local state. Genuine removals, where the record is no longer linked, are still sent.
opw-6401146
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prMaintenance request activities now use the user's timezone when setting deadlines from scheduled dates. This prevents reminders from appearing one day early for users in timezones far ahead of UTC, improving scheduling accuracy.
Original PR description
Steps to reproduce: =============== - Install `maintenance` module - Set your user's timezone to Pacific/Auckland (UTC+12/+13). - Go to Maintenance > Maintenance Requests > New. - Set the Scheduled…
Steps to reproduce:
===============
- Install `maintenance` module
- Set your user's timezone to Pacific/Auckland (UTC+12/+13).
- Go to Maintenance > Maintenance Requests > New.
- Set the Scheduled Date to tomorrow morning (e.g. 8:00 am).
- Save and check the auto-generated activity in the chatter.
Issue:
====
The activity generated for the maintenance request's Scheduled Date is
created with a deadline of Today instead of Tomorrow, even though the
Scheduled Date was explicitly set to Tomorrow.
Cause:
=====
`schedule_date` is a `Datetime` field, always stored in UTC. In
`MaintenanceRequest.activity_update()`, the deadline for the activity
(a `Date` field) was computed with:
```py
fields.Datetime.from_string(request.schedule_date).date()
```
`from_string()` only parses the stored value; it does not convert it
to any timezone. Calling `.date()` on it therefore truncates the raw
UTC datetime directly, ignoring the timezone of the user who picked
the date.
For a user ahead of UTC by a large enough offset (e.g. Pacific/Auckland,
UTC+12/+13), picking "Tomorrow 8:00 am" in their local timezone can
resolve to a UTC instant that still falls on Today's date (Tomorrow
8:00 local minus 13 hours lands at 19:00 UTC on Today). Truncating
that UTC value straight to a date silently shifts the deadline back
by one full day versus what the user selected.
Fix:
===
The deadline is now computed from the schedule date after converting
it to the record's (user's) timezone, using
`fields.Datetime.context_timestamp()`, instead of truncating the raw
UTC value. This fixes the reported issue for any user whose timezone
offset makes the truncation land on the wrong side of midnight UTC.
---
Note - automatic activity creation is remove from this [commit](https://github.com/odoo/odoo/commit/ab63e9709d8ea790a8a72a0a86b8bccc478c02d5) in saas-19.2
---
opw-6363868
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prRomanian SAF-T (D406) export files now show the correct required file version, 2.0, instead of 2.4.8. This helps businesses submit compliant accounting reports and avoid potential validation or filing issues.
Original PR description
**Steps to reproduce:** - Install the `l10n_ro_saft` module and switch to the RO Company. - Navigate to Accounting > Reporting > General Ledger. - Click the gear icon > SAF-T (D406 Declaration). - Open the generated XML file and check the `AuditFileVersion` node. **Observation:** The `AuditFileVersion` node is set to `2.4.8`. **Expected behavior:** The `AuditFileVersion` node should be set to `2.0` (confirmed with the PO [1]) **Root Cause:** At [2], the `file_version` value is incorrectly set to `2.4.8` instead of `2.0`. [1]: https://www.odoo.com/mail/message/1144385840 [2]: https://github.com/odoo/enterprise/blob/ce2ad80c91aea27b143a80018aa73ed10d16cdbe/l10n_ro_saft/models/account_general_ledger.py#L179-L183 opw-6452918
The Send to eTransport button is now shown when a Romanian stock transfer is ready as well as when it is completed. This lets users start the required eTransport reporting at the right operational stage instead of waiting until the transfer is done.
Original PR description
Currently, the Send to eTransport button on `stock.picking` is only visible when picking is done. This PR fixes this behaviour and makes it visible when picking is ready or done both. task-5930984 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update improves the internal tests that verify video meeting links are correctly included in calendar invitation files. It helps maintain reliability for online meeting invitations without changing the user-facing calendar experience.
Original PR description
Follow-up of #283562. PR above added the meeting's videocall_location as a URL property in generated iCalendar (.ics) invitation files. This commit clean the code of tests written in the forward-port (#283934).
Fixes an issue where Factur-X e-invoices received through Peppol could fail to import properly because the embedded XML was not read before checking the invoice type. This prevents empty or incomplete records from being created and improves handling of self-billed invoices in the French e-invoicing flow.
Original PR description
When importing new documents from Peppol into the database, we determine whether they are self-billed by checking a Type Code in the XML file. Factur-X is an hybrid format where the XML is embedded inside a PDF. Currently, we are not extracting the XML before searching for that Type Code, and it leads to an error that prevents the document from being imported correctly: - V17, V18: An empty invoice is created and linked with the attachment. - V19+: Only the attachment is created. Additionnaly, we only check for InvoiceTypeCode or CreditNoteTypeCode, but the CII XML format embedded inside the hybrid Factur-X format use TypeCode instead. This PR aims at fixing both these issues. Ticket: opw-6417682 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280714
Credit card and cash journals configured for file-based transaction imports now show the Import File button on the Accounting dashboard. This removes confusion and lets users import statements consistently across supported journal types.
Original PR description
The "Import File" button on the dashboard card only shows up for bank journals. Credit card journals are treated the same way as bank journals pretty much everywhere else: the dashboard card itself, the statements list, the "Transaction Feeds" setting on the journal form (where you can pick the file import option), and even `create_document_from_attachment` in this module, which already accepts them. So you end up with a credit card journal set to import files but nothing on its card to actually do it, and people assume the feature is simply not there. Show the button on credit card journals too. Steps to reproduce: - Install account_bank_statement_import_csv (or any other import format) - Create a "Credit Card" journal and set its Transaction Feeds to the file import option - Go to the Accounting dashboard - The bank journal card has an "Import File" button, the credit card one doesn't --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
New customer or vendor records created from sales or purchase orders now start as companies instead of individuals. This prevents avoidable validation issues during setup and makes the flow consistent with invoicing behavior.
Original PR description
When creating a new customer or vendor from a sale order or purchase order, the partner form can initially be created with company_type set to person. This can trigger constraints on stored fields before the user switches the partner type to Company. Set default_is_company=True in the partner field context for Sale and Purchase, matching the existing behavior in Account. This ensures newly created customers and vendors default to Company.
This fixes an issue where Odoo could use the wrong path separator when handling static files on Windows. It helps ensure static resources load correctly for Windows-based deployments without affecting normal use on other systems.
Original PR description
In commit 31aad6c, path normalization was added which also resulted in `/` being converted into `\` on Windows. There the `path.split('/')` did not work.
This commit changes the `'/'` to `os.sep` to fix the issue.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285216This fix ensures product variant prices inherit the same minimum decimal display settings as their product templates. Reports and Studio customizations now show prices consistently, avoiding confusing differences such as 19.50 appearing as 19.5.
Original PR description
Issue: --- Record's `min_display_digits` is not inherited in case of a model inheritance. e.g. `list_price` is inherited to `product.product` from `product.template`, however it renders with fewer decimals than `product.template.list_price` in reports. Steps to reproduce: 1- Set a product's Sales Price to 19.5. 2- Add `product.product.list_price` to a report using studio. The variant's field renders with 1 decimal while `decimal.precision` is 2. Cause: --- This is missed in ee32a17495a10af18f0b5de0a295283c5059d3c2 which introduces minimum precision. opw-6465159
This fixes an issue where applying text color to a partial selection could color extra nearby text by mistake. The HTML editor now applies color only to the exact content selected, improving editing accuracy and preventing unintended formatting changes.
Original PR description
Problem: When applying color on a selection that spans across partially selected inline elements (e.g. `<p><b>a[b</b>c]d</p>`), container elements whose contents are not fully selected (such as…
Problem: When applying color on a selection that spans across partially selected inline elements (e.g. `<p><b>a[b</b>c]d</p>`), container elements whose contents are not fully selected (such as `<b>`) were included in `targetedNodes`. This caused improper color formatting/nesting on partially selected elements. Cause: In `ColorPlugin._applyColor()`, `targetedNodes` were filtered by checking `isNodeEditable(node)` and `nodeName !== "T"`, but did not check whether the contents of `node` were fully selected (`areNodeContentsFullySelected(node)`). As a result, partially selected ancestor elements were included in `targetedNodes`. Solution: Filter `targetedNodes` in `_applyColor()` using `this.dependencies.selection.areNodeContentsFullySelected(node)` to ensure only fully selected nodes are targeted when applying colors. Steps to reproduce: 1. Open html_editor. 2. Insert content: `<p><b>ab</b>cd</p>`. 3. Select `b` inside `<b>` and `c` inside `<p>` (`<p><b>a[b</b>c]d</p>`). 4. Apply text color (e.g. red). 5. Observe "ab" and "c" was colored instead of just "b" and "c". task-6456443 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix corrects Malaysian payroll calculations for SOCSO and Employment Insurance contributions so employee net pay matches expected official contribution amounts. It also resolves duplicated deductions and aligns employer contribution display signs, improving payroll accuracy and reporting clarity.
Original PR description
There was a difference between the actual and expected NET. **Causes** - rules existed for both SOCSO Act 800 and EIS, but Act 800 seems to be the official name of the Employment Insurance System…
There was a difference between the actual and expected NET. **Causes** - rules existed for both SOCSO Act 800 and EIS, but Act 800 seems to be the official name of the Employment Insurance System (source: https://www.perkeso.gov.my/images/dokumen/Rate_of_Contribution_ACT_800.pdf). Keep the ACT 800 one as its matches the expected amount. - there was a double counting of the SOCSO employee contributions (4 and 800) , as `l10n_my_rule_socso_employee` (the sum of both) was added to the total deductions. **Change** Before: 3500/month wage results in a 3108 NET. <img width="1181" height="533" alt="before" src="https://github.com/user-attachments/assets/cdf4420f-e2cf-4dc7-9488-6bde3f1f9961" /> After: 3500/month wage results in a 3090.85 NET: - 6.90 SOCSO Act 800 Employee - 6.90 SOCSO Act 800 Employer - 17.25 SOCSO Act 4 Employee - 60.35 SOCSO Act 4 Employer Which seems consistent with online sources (https://payroll.my/) <img width="1181" height="425" alt="after" src="https://github.com/user-attachments/assets/7fb41186-dbb4-4d7a-ac9a-14fb56efc4bc" /> Other fix: while not affecting the calculation, 'SOCSO Employer Share' appeared as incorrect, the two rules SOCSO_800_EMPLR and SOCSO_4_EMPLR should have the same sign. opw-5976362
Refreshing an X (Twitter) feed now handles invalid account tokens by disconnecting the affected account instead of showing a generic error. This prevents users from being blocked by unclear error messages and guides them toward reconnecting the account.
Original PR description
Bug === If the token because invalid, then an UserError is raised instead of disconnecting the account. This is because we "blind raise" all errors we get from X, instead of filtering the error linked to the stream configuration. Task-6499283 Forward-Port-Of: odoo/enterprise#129110
Users now see a more accurate message when requesting a migration in the French PDP configuration. This avoids misleading expectations from the previous “available tomorrow” timeline and makes the process clearer for everyone.
Original PR description
Currently when the user requested a migration, we don't show it in any way to the user, and the only timeline we give ("available tomorrow") is completely false. Because we don't have any field we could use for this client-side (maybe from 19.3 we can use the catch-all-json field) So just make the message same for everybody.
no-task
Forward-Port-Of: odoo/odoo#283594This fixes a navigation problem in Helpdesk where opening tickets from a team reached through an email alias could show an error instead of the ticket list. The system now uses the correct Helpdesk Team information, so users can access related tickets normally.
Original PR description
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe…
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe error ### Description of the issue/feature this PR addresses: **Issue:** When navigating from an email alias to its parent document (e.g., a Helpdesk Team), the web client incorrectly retains the `active_id` and `active_model` of the alias in the context. **Solution:** We updated the `action_view_ticket` method in `helpdesk.team` to explicitly inject the correct `active_model` and `active_id` into the context before calling `_for_xml_id`. This overwrites the polluted Alias data before the window action is evaluated. ### Current behavior before PR: Clicking the "Tickets" smart button passes the old alias context into the action. This bad data flows into the ticket view, which attempts to look up a Helpdesk Team using the Alias's ID to generate the empty list help message, resulting in a `MissingError`. ### Desired behavior after PR: The Python action sanitizes the context at the source, ensuring that the XML action and subsequent view evaluations receive the correct Helpdesk Team ID. Ultimately, the view will load normally without crashing. opw-6395638
This fix keeps timesheet entries connected to the correct replacement invoice when an invoice is reversed and recreated through a credit note. It helps ensure businesses retain accurate billing traceability for timesheet-based services and prevents replacement invoices from losing their supporting timesheet references.
Original PR description
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior…
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior before PR: When reversing an invoice tied to timesheets and creating a replacement via a credit note, the timesheets linked to the original invoice have their timesheet_invoice_id cleared. Because the modify_moves function builds the replacement invoice directly via copy_data()/create(), it bypasses the normal sale order invoicing flow. As a result, the unbilled timesheets are left permanently unlinked from the newly created invoice, leaving the new invoice with no reference to the timesheets linked to the original sale order. ### Desired behavior after PR is merged: When a replacement invoice is created, each timesheet is properly relinked to the corresponding line on the new invoice. This linkage matches on the sale order line (so_line) rather than line position, ensuring accuracy since line order and count are not guaranteed to be preserved between the original and modified invoices. opw-6449995 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283104
This fix prevents an unexpected error from appearing when Odoo prepares certain report actions. It improves reliability for users by ensuring the system uses the correct record context instead of failing with a traceback.
Original PR description
opw-6360013 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#274977
Fixes an error that could block updates to manufacturing work orders when one task had already started and another task's duration was changed. This keeps production teams able to start, pause, complete, and adjust work orders reliably from the list view.
Original PR description
On a not-planned manufacturing order with at least 2 workorders:
- start one of them (this makes 'is_planned' true)
- change the duration of another one
This gives the error:
TypeError:
'<' not supported between instances of 'datetime.datetime' and 'bool'
Origin is within _set_duration where no leave is created when changing
the state for 'progress'.
This issue occurs within _plan_workorders when a workorder is ready
with no leave_id, so fix in this function to handle future origins.
Note that if none of the workorders are started, the error does not occur.
Workorders list view has been fixed to accomodate Start, Pause & Done buttons on such workorders.
opw-4497704
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr