Wednesday, August 12, 2026
3 changes · saas-19.1
Resolved issues and error corrections
Refunding Ecuador POS orders made with the “Consumidor Final” customer now shows the intended warning instead of crashing. This helps cashiers understand why the refund cannot be validated and avoids an unexpected interruption at checkout.
Original PR description
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to…
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to reproduce: ------------------- In POS with l10n_ec_edi module activated: * Create a new order with "Consumidor Final" as customer * Add products and pay the order * Validate the order * Attempt to refund this order > Observation: The refund validation would crash with: TypeError: Cannot read properties of undefined (reading 'add') at OrderPaymentValidation.isOrderValid Why the fix: ------------ The code was trying to access `this.dialog` which is undefined in the OrderPaymentValidation class context. The dialog service should be accessed via `this.pos.dialog`, which is the correct pattern used throughout the base OrderPaymentValidation class. This fix ensures the error dialog is properly displayed when attempting to refund orders for the anonymous final consumer, instead of crashing with a TypeError. opw-6427113 Forward-Port-Of: odoo/enterprise#126308
This fixes an issue where unused leave could be missed when processing payslips for multiple employees at once. Each payslip now uses the correct employee’s leave allocations, helping ensure Australian payroll amounts are calculated accurately.
Original PR description
`_l10n_au_get_unused_leave_by_type` compared leave allocations to `self.employee_id` while looping payslips. On a multi-recordset that is the whole employee set, so the match never holds and unused leave is skipped. Use `payslip.employee_id` so each payslip keeps its own allocations. task-6458480 Forward-Port-Of: odoo/enterprise#127330
Fixes an issue where using AI to update website SEO could fail when the AI agent relied on certain document-generated files, such as invoice PDFs. The AI feature can now correctly use those document sources, reducing interruptions for users optimizing website content.
Original PR description
### Problem When an AI agent includes a document source whose underlying `ir.attachment` has `res_field` set (e.g., an invoice PDF generated from a Sale Order), triggering **Update With AI** from…
### Problem
When an AI agent includes a document source whose underlying `ir.attachment`
has `res_field` set (e.g., an invoice PDF generated from a Sale Order),
triggering **Update With AI** from **Website → Site → Optimize SEO**
raises a `KeyError` in `_build_rag_context`.
### Steps to Reproduce
1. Open the **AI** app.
2. Configure the **Odoo Agent**.
3. Add a source → **Add From Documents**.
4. Select a document whose underlying `ir.attachment` has `res_field` set
(e.g., an invoice PDF generated from a Sale Order).
5. Go to **Website → Site → Optimize SEO**.
6. Click **Update With AI**.
7. Observe the following error:
```
KeyError: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```
[Video](https://drive.google.com/file/d/1AAg0AvC3TRQzMLhI9hWFuSarOTHzQE-b/view?usp=sharing)
### Root Cause
In `ai/models/ai_agent.py`, `_build_rag_context()` retrieves the
`ai.agent.source` records corresponding to the embeddings by searching on the
attachment checksum:
```python
agent_sources = self.env["ai.agent.source"].search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
```
This domain traverses `attachment_id.checksum`, which internally calls
`ir.attachment._search()`.
As part of the standard attachment search behavior,
`ir.attachment._search()` automatically injects a
`('res_field', '=', False)` filter unless:
- `skip_res_field_check=True` is set in the context,
- the domain explicitly references `id` or `res_field`, or
- `bypass_access` is enabled.
```python
domain = Domain(domain)
if (
not self.env.context.get("skip_res_field_check")
and not any(d.field_expr in ("id", "res_field") for d in domain.iter_conditions())
and not bypass_access
):
disable_binary_fields_attachments = True
domain &= Domain("res_field", "=", False)
```
[Reference](https://github.com/odoo/odoo/blob/19.0/odoo/addons/base/models/ir_attachment.py#L630)
Because of this implicit filter, attachments with `res_field` set are excluded
from the search. Consequently, `agent_sources` does not contain all the sources
corresponding to the retrieved embeddings.
Later, `_build_rag_context()` builds a checksum-to-source mapping:
```python
source_map = {
source.attachment_id.checksum: source
for source in agent_sources
}
for embedding in similar_embeddings:
checksum = embedding.attachment_id.checksum
agent_source = source_map[checksum]
```
Since `source_map` is built from the incomplete `agent_sources` recordset, it is
missing entries for attachments filtered by `ir.attachment._search()`.
However, `similar_embeddings` still contains embeddings for those attachments.
As a result, the lookup:
```python
agent_source = source_map[checksum]
```
raises a `KeyError`.
### Solution
Bypass the implicit `res_field` filter when searching `ai.agent.source`:
```python
agent_sources = (
self.env["ai.agent.source"]
.with_context(skip_res_field_check=True)
.search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
)
```
This ensures that all `ai.agent.source` records matching the requested
attachment checksums are returned, including those referencing attachments with
`res_field` set. As a result, `source_map` contains all expected entries and
`_build_rag_context()` no longer raises a `KeyError`.
opw-6379816