Daily updates from Odoo
Friday, August 7, 2026
12 changes · 19.0
Enhancements to existing features
All signers can now choose whether to add a frame when adopting their signature, not only internal Odoo users. The signing dialog controls also have clearer borders, making the public signing experience easier to use and understand.
Original PR description
Version: 19.0 Before this PR: The 'Frame' checkbox in the 'Adopt Your Signature' dialog was only shown to internal Odoo users (users with the `base.group_user` group).Also, on the public signing page, the Full Name input, the Frame checkbox and the Auto/Draw/Load buttons had no visible border After this PR: The 'Frame' checkbox is now rendered for every signer in the 'Adopt Your Signature' dialog. The Full Name input, Frame checkbox and Auto/Draw/Load buttons now have a visible border. Taskid-4610728
Resolved issues and error corrections
Users who have both Partner Commissions access and Purchase user access can now create and view purchase orders as expected. This prevents commission-related permissions from unintentionally blocking normal purchasing work, while keeping commission-only restrictions in place.
Original PR description
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new…
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new Purchase Orders, and existing Purchase Orders are also not visible in the Purchase module. ## Expected behavior: The expected behavior is that the user should be able to create and view Purchase Orders with these access rights. Additionally, clarification is required regarding the purpose of the new Partner Commissions access group. ## Steps to reproduce: - Go to user and assign Partner Commission rights as All or own document. - On Purchase, select group as User. ## Cause of the issue: partner_commission adds commission-specific purchase order record rules, but purchase users have no matching purchase-order rule in that module. For mixed-role users, the commission rule ends up restricting standard purchase orders as well. ## Fix: Apply the module's explicit all-purchase rule to purchase users so mixed users keep base procurement access while commission-only users remain restricted by the commission rules. opw-6366074
TikTok Shop orders that arrive without a payment time, common with cash-on-delivery payments, will no longer fail during import. Odoo now uses the order creation time temporarily and updates the order once payment confirmation is later received.
Original PR description
**Issue**
- TikTok Shop orders may be received without any `paid_time` value in the returned API data, particularly for _Cash On Delivery ([COD](https://seller-ph.tiktok.com/university/essay?knowledge_id=10004482&lang=en)) payments.
- When creating the order, `_create_order_from_data` accessed `order_data[‘paid_time’]` directly, which triggered a `KeyError` and prevented all orders from being fully synchronised.
**Changes**
- When creating an order, if `paid_time` is missing from the data provided by TikTok:
-> `date_order` is initialised with `create_time` as a fallback
-> The new field `tiktok_payment_pending` is set to `True` to mark the order as awaiting payment confirmation.
- During subsequent synchronisations, if `paid_time` is now provided by TikTok for a pending order,
-> `date_order` is updated with the new provided timestamp and the `tiktok_payment_pending` flag is reset.
opw-6440127This fixes an issue where Instagram posts without a media URL could stop Social Marketing from syncing and show an error when the app opened. The system now safely handles missing media links so Instagram content continues to sync as expected.
Original PR description
The fix introduced in https://github.com/odoo/enterprise/commit/9e9c99712ad4b9d58dc7601da41a852e457ad097 didn't account for the fact that `post.get('media_url') ` could return a None value, which in…
The fix introduced in https://github.com/odoo/enterprise/commit/9e9c99712ad4b9d58dc7601da41a852e457ad097 didn't account for the fact that `post.get('media_url') ` could return a None value, which in turn would raise en error when trying to concatenate the value later.
This in turn:
- will block syncing of instagram instagram posts
- will raise a traceback when you open the Social Marketing module and the auto-sync kicks in.
### Example traceback
```
Traceback (most recent call last):
[...]
File "/home/odoo/src/enterprise/saas-19.2/social_instagram/models/social_stream.py", line 86, in _fetch_stream_data
return self._fetch_instagram_posts()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/social_instagram/models/social_stream.py", line 64, in _fetch_instagram_posts
values['message'] = (values['message'] + "\n" + post.get('media_url')).strip()
~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
TypeError: can only concatenate str (not "NoneType") to str
```
### Solution:
Fallback to an empty string if `post.get('media_url')` yields a None value.
OPW-6449357
Forward-Port-Of: odoo/enterprise#127049
Forward-Port-Of: odoo/enterprise#126987Fixes a DHL Express shipping issue where international deliveries with multiple packages could be blocked during validation even though the carrier response was valid. The system now treats DHL Express's multi-package rate notice as informational, while still blocking real shipping errors.
Original PR description
Steps to reproduce --- 1. Configure a `delivery_easypost` shipping method using the DHL Express carrier. 2. On an international delivery, use Put in Pack to create two or more packages. 3. Validate…
Steps to reproduce --- 1. Configure a `delivery_easypost` shipping method using the DHL Express carrier. 2. On an international delivery, use Put in Pack to create two or more packages. 3. Validate the delivery. Issue --- Validation is blocked with `DHLExpress: rate_error -- DHLExpress multi-shipment rate includes this shipment.` For an EasyPost multi-shipment order, DHL Express returns the aggregate rate on the first (master) shipment only and adds an informational `rate_error` on the order stating that this rate already covers the whole order, so the remaining shipments carry no rate of their own. The message is not a real error, but `send_shipping` raises on any carrier message whose type is not exempt: https://github.com/odoo/enterprise/blob/534b42def8ae5dc884da4398377f828c49557d6e/delivery_easypost/models/easypost_request.py#L359-L364 `_post_process_ship_response` already detects and clears exactly this harmless message, but only for a hardcoded carrier allowlist introduced in a2de5bc5a14 (`Purolator`, `DPD UK`, `UPS`) that was never extended to DHL Express, so for DHL Express the message survives, reaches the raise, and aborts an otherwise valid shipment: https://github.com/odoo/enterprise/blob/a2de5bc5a14f99b8674c3ee234e4d221b406da0f/delivery_easypost/models/easypost_request.py#L416-L434 `DHL Express` is added to that allowlist so the harmless multi-shipment `rate_error` is posted on the picking and cleared instead of raised. The guard still requires a single `rate_error` carrying the "multi-shipment rate includes this shipment." text with the rate present only on the master shipment, so genuine DHL Express errors keep blocking validation. opw-6450365
VoIP calls now wait longer to complete connection setup, reducing cases where one side cannot hear the other. Additional debug logging helps support teams diagnose audio connection issues faster when problems occur.
Original PR description
The 1-second ICE gathering timeout was often too short to complete a
STUN round-trip, especially for agents answering an incoming call. When
it expired, SIP.js sent the SDP answer with only local/private ("host")
candidates, missing the public (srflx) one.
In debug mode, forward the RTCPeerConnection events relevant to
ICE/media issues (onicecandidate, onicecandidateerror,
oniceconnectionstatechange, onicegatheringstatechange,
onnegotiationneeded, onsignalingstatechange, onconnectionstatechange) to
the console, wired as soon as the SessionDescriptionHandler exists so
gathering-time events aren't missed.
Also periodically (every 5s) log a summary of getStats() while the call
is ongoing, to see whether RTP packets are actually flowing in each
direction.
task-6450278Fixes an issue where completing a field service task could freeze or fail after a delivery was returned and then re-delivered. The system now avoids repeatedly checking the same stock movements, preventing memory errors and keeping task completion reliable.
Original PR description
## **Steps to reproduce:** 1. Create a Service product with Create on Order set to Task and Project set to Field service project. 2. Create a Sales Order containing a storable product and a service…
## **Steps to reproduce:** 1. Create a Service product with Create on Order set to Task and Project set to Field service project. 2. Create a Sales Order containing a storable product and a service product. 3. Confirm the Sales Order to generate the project, task, and delivery order. 4. Validate the delivery order. 5. Create and validate a return for the delivery. 6. Create a return of the return to deliver the products again, but do not validate this new delivery. 7. Open the related task and click on Mark as Done button. ## **Issue:** In a delivery -> return -> return of return workflow, the stock move goes into this code https://github.com/odoo/enterprise/blob/59b86f106862c3a364ba633a1580d9051f2fe7ca/industry_fsm_stock/models/project_task.py#L89-L90 the traversal repeatedly revisits the same stock moves through move_dest_ids, causing the loop to alternate between the same move recordsets indefinitely. As a result, the loop never terminates, eventually exhausting the memory and raising a `MemoryError`. ## **Solution:** Track the stock moves that have already been visited and continue the traversal only with unseen destination moves. Runbot Video : [Video](https://drive.google.com/file/d/1wiqggjx8T-Mtl4T1JgCsfwBbgQU18nYF/view?usp=drive_link) OPW - 6420961
Employees on flexible schedules can now record short time off periods without the system treating the entire day as leave. This keeps overtime and Time Off calendar views accurate, so worked hours and leave hours are reported correctly.
Original PR description
Problem: On a flexible working schedule, a time off of a few hours (neither a full nor a half day) was treated as a full day off. The Attendance list then reported the whole day's attendance as…
Problem: On a flexible working schedule, a time off of a few hours (neither a full nor a half day) was treated as a full day off. The Attendance list then reported the whole day's attendance as overtime, and the Time Off gantt grayed out the entire day instead of only the leave's hours. Steps to reproduce: 1. Give an employee a flexible working schedule (e.g. 8h/day) with an overtime ruleset based on the contract's expected hours. 2. Record a 2-hour time off, then an 8-hour attendance on the same day. 3. Observe the attendance reports 8 hours of overtime instead of 2. Current behavior: A partial time off makes the whole day count as extra hours. Expected behavior: Only the hours actually taken off reduce the day's expected hours. Cause: For a flexible schedule, _handle_flexible_leave_interval expands a leave to the whole day. The override already narrows full-day and half-day leaves, but any other number of hours fell through to that full-day expansion. The expanded interval is subtracted from the day's expected hours in _work_intervals_batch, so they drop to zero and every worked hour becomes overtime. Fix: A leave of an arbitrary number of hours should only remove the hours it actually covers, so it keeps its requested interval instead of being stretched to the whole day. opw-6291536
Dutch SBR tax return exports now use the Tax Unit VAT number when a Tax Unit is selected, instead of incorrectly using the individual company OB number. This prevents rejected XBRL filings for fiscal unity registrations while keeping the company OB number behavior for single-company reports.
Original PR description
**Steps to reproduce:** * Install the **Netherlands - SBR** (`l10n_nl_reports_sbr`) and **Netherlands - SBR OB Nummer** (`l10n_nl_reports_sbr_ob_nummer`) modules. * Create two companies with Dutch…
**Steps to reproduce:** * Install the **Netherlands - SBR** (`l10n_nl_reports_sbr`) and **Netherlands - SBR OB Nummer** (`l10n_nl_reports_sbr_ob_nummer`) modules. * Create two companies with Dutch localization. * Go to **Accounting → Configuration → Tax Units** and create a Tax Unit with its own **Tax ID** (e.g. `NL826317558B01`), adding both companies. * On the main company form, fill in the **Omzetbelastingnummer** field (e.g. `123456782B90`). * Go to **Accounting → Reporting → Tax Return**, select the Tax Unit in the filter, and click **XBRL → Download XBRL File**. **Observed behavior:** * The `<xbrli:identifier>` in the exported XBRL file contains the company's **Omzetbelastingnummer** (`123456782B90`) instead of the Tax Unit's VAT (`826317558B01`). * The tax authority rejects the return because the identifier does not match the fiscal unity registration. **Cause:** * `_get_sbr_identifier()` in `l10n_nl_reports_sbr_ob_nummer` unconditionally returns `self.env.company.l10n_nl_reports_sbr_ob_nummer` before consulting the Tax Unit. * The `super()` call, which correctly routes to `tax_unit.vat` via `report.get_vat_for_export()`, is only reached when the company field is empty — so the Tax Unit's VAT is never used when a company OB-number is set. **Fix:** * When a Tax Unit is active in the report options, delegate immediately to `super()._get_sbr_identifier()`, which resolves `tax_unit.vat` through the existing `get_vat_for_export()` logic. * The company-level `l10n_nl_reports_sbr_ob_nummer` override is preserved as a fallback for the `company_only` (no Tax Unit) case. opw-6350840 Forward-Port-Of: odoo/enterprise#126999 Forward-Port-Of: odoo/enterprise#125167
Fixes an issue where Uruguayan electronic delivery guide PDFs did not include a separate addenda page when the addenda text was too long for the standard box. This ensures legal delivery documents show all required additional information, improving compliance and document completeness.
Original PR description
**Description** When printing the legal PDF of a CFE, the report can request a dedicated addenda page (`adenda=true` report parameter) when the addenda does not fit in the small box of the standard…
**Description** When printing the legal PDF of a CFE, the report can request a dedicated addenda page (`adenda=true` report parameter) when the addenda does not fit in the small box of the standard report (roughly 6 lines of 140 characters). `l10n_uy_edi_document._get_report_params()` computed the addenda by calling `self.move_id._l10n_uy_edi_get_addenda()` directly. For e-remito EDI documents created from stock pickings, `move_id` is not set, so the addenda was always empty and the dedicated addenda page was never requested for delivery guides. **Changes** - Resolve the addenda from the document origin record: reuse the existing `_get_origin_record()` abstraction. Add it on `l10n_uy_edi.document` (returns the move) and let `l10n_uy_edi_stock` extend it to fall back to `picking_id`; `_get_report_params()` now reads the addenda through it. - Remove a no-op `_get_pdf()` override in `l10n_uy_edi_stock`. - Add a unit test covering the dedicated addenda page threshold (6 lines x 140 chars) for delivery guides. **Note** This PR replaces branch `adhoc-dev:18.0-t-stock-edi-addenda-fix-kz` (original authorship preserved); resubmitted from a new branch to keep follow-up and tracking with the current maintainer. Forward-Port-Of: odoo/enterprise#124539
VoIP contact searches and keypad suggestions now also compare entered numbers with standardized phone numbers, not only the saved raw phone field. This helps users find the right contact even when country codes are automatically added or stored differently.
Original PR description
Before this fix, the keypad's callee suggestions only matched the search term against the raw `phone` field of contacts. When the user input was automatically prefixed with a country code (e.g. +86), the match could fail if the stored phone number lacked the international prefix. Now `phone_sanitized` is also sent to the frontend via the Store, and the callee suggestion matching falls back to the E164 sanitized number when the raw phone field does not match. Task-6395518 compr https://github.com/odoo/odoo/pull/278018
Dutch VAT correction filings will now be sent to Digipoort using the correct OBSUP message type instead of being treated as regular VAT reports. This prevents routing errors and helps ensure correction submissions reach the right destination.
Original PR description
Description of the issue this commit addresses: VAT corrections are sent to Digipoort as regular VAT reports, causing routing errors. --- Desired behavior after this commit is merged: This commit sends VAT corrections using the OBSUP message type. --- opw-6353166