Daily updates from Odoo
Monday, August 10, 2026
17 changes · 18.0
Enhancements to existing features
Belgian payroll now includes the upcoming fiscal employment bonus rate changes for low-wage workers from August 2026 and the general rate increase from 2028. This helps payroll calculations stay aligned with future Belgian tax rules and reduces manual compliance updates.
Original PR description
Starting from August 2026: - The increased fiscal rate for low-wage workers (Volet B) rises from 52.54% to 63% (and to 72% in 2028). - The general fiscal rate (Volet A) rises from 33.14% to 35% starting in 2028. This adds new rule parameters for the fiscal rates and updates computation logic to apply these rates Task-6438319 Forward-Port-Of: odoo/enterprise#126713
Resolved issues and error corrections
Tests related to excluding customers from follow-up actions were moved to the correct module. This helps ensure the follow-up feature works for customers who do not use the optional no-follow-up module and prevents avoidable errors in invoice printing checks.
Original PR description
`no_followup` is a field defined in `account_no_followup` that used in commit https://github.com/odoo-dev/enterprise/commit/2c1164bb9888f8dbc7d434a95b4be6d42c0f9143 in the module `account_followup`. This leads to issues where customers that don't have the module `account_no_followup` installed can't call `_get_invoices_to_print` without getting an error Fixed by https://github.com/odoo-dev/enterprise/commit/de73ef1d2b9a52fdfef4f5c5e28c7876e927a6b8 This commit moves the tests in the appropriate module opw-6402268
Australian payroll now correctly checks unused leave for each individual employee when preparing payslips. This prevents unused leave from being missed when multiple payslips are processed together, improving payroll accuracy.
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-6451508
This update prevents an error when users customize worksheet design templates in Studio after upgrading from an older version. It ensures the correct company-specific worksheet template is selected, so businesses with multi-company setups can continue editing templates without disruption.
Original PR description
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field. For example, in v17, a single worksheet template linked to 3 companies via the m2m field…
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field.
For example, in v17, a single worksheet template linked to 3 companies via the m2m field was returned as 1 record when opening Design Template. After the upgrade in v18, company_id became m2o, and the same data is split into 3 separate records (one per company).
When trying to add a customization via Studio, the search [fetches](https://github.com/odoo/enterprise/blob/18.0/worksheet/controllers/main.py#L12) records based on the model set on the worksheet. In the new version, Studio
[creates](https://github.com/odoo/enterprise/blob/18.0/worksheet/models/worksheet_template.py#L112)
a new model, but for existing records the
model is the same across the 3 worksheet records tied to the same template. This causes the search to match all 3 records and raise a SingletonError.
```py
Traceback (most recent call last):
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2856, in __call__
response = request._serve_db()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2331, in _serve_db
raise self._update_served_exception(exc)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2329, in _serve_db
return service_model.retrying(serve_func, env=self.env)
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 188, in retrying
result = func()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2384, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2599, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "/home/odoo/src/odoo/19.0/odoo/addons/base/models/ir_http.py", line 353, in _dispatch
result = endpoint(**request.params)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/industry_fsm_report/controllers/main.py", line 9, in edit_view
action = super().edit_view(view_id, studio_view_arch, operations, model, context)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/worksheet/controllers/main.py", line 17, in edit_view
worksheet_template_to_change._generate_qweb_report_template()
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 490, in _generate_qweb_report_template
new_arch = self._get_qweb_arch(worksheet_template.model_id, report_name, form_view_id)
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 460, in _get_qweb_arch
if 'name' in row_node.attrib and row_node.attrib['name'] not in self._get_qweb_arch_omitted_fields() and row_node.attrib['name'] in form_view_fields:
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 378, in _get_qweb_arch_omitted_fields
'x_%s_id' % self.res_model.replace('.', '_'), 'x_name', # redundant
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1657, in __get__
record.ensure_one()
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5942, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: worksheet.template(3, 14, 18)
```
OPW: 6389190This fix prevents barcode test failures when Romanian stock localization demo data is installed. It keeps automated checks reliable without changing day-to-day warehouse barcode workflows for users.
Original PR description
Runbot was failing in romania localization with demo data throwing error: ``` FAILED: [4/9] Tour test_select_with_same_product_and_lot → Step .o_barcode_line.o_selected .o_barcode_scanner_qty…
Runbot was failing in romania localization with demo data throwing error:
```
FAILED: [4/9] Tour test_select_with_same_product_and_lot →
Step .o_barcode_line.o_selected .o_barcode_scanner_qty .qty-done:contains('1')
{
'trigger': '.o_barcode_line.o_selected .o_barcode_scanner_qty .qty-done:contains('1')',
'run': 'scan lot_xyz'
},
```
## Root cause:
This issue occurs when `l10n_ro_saft_stock` is loaded with demo data this triggers _get_picking_type_create_values [1] which makes scanning destination location mandatory.
After which `_check_multi_warehouse_group` is called which enables multi location at [2] since demo data also has a warehouse called `Romanian warehouse`.
Both of these settings cause scanning source location to be mandatory when opening the barcode app at `_get_barcode_config` as shown at [3]
This error does not occur in versions 18.3+ due to changes on how demo data is loaded while running test cases.
[1]-
https://github.com/odoo/enterprise/blob/df42d04ac1c9717d71d48e974c67f3629968fa3a/stock_barcode/models/stock_warehouse.py#L9
[2]-
https://github.com/odoo/odoo/blob/638bb3194246f9d33741c66e14aa6b9b34f5e5e8/addons/stock/models/stock_warehouse.py#L331-L335
[3]-
https://github.com/odoo/enterprise/blob/df42d04ac1c9717d71d48e974c67f3629968fa3a/stock_barcode/models/stock_picking_type.py#L106
## Solution:
Always scan source barcode location, if demo data does not have source location to be mandatory it will raise a simple toaster notification which has no effect on overall flow of the tour, but if source location scanning is mandatory as in case of `l10n_ro_saft_stock` the tour will scan source location and continue the flow as usual.
[runbot-938994](https://runbot.odoo.com/odoo/error/938994)This update adds test coverage showing that addenda information was not working correctly for Uruguayan electronic delivery remittances. It helps ensure the issue can be verified and corrected, reducing the risk of incomplete electronic shipping documents.
Original PR description
unit tests test that proveed the addenda is not working for e-rem
This fixes an issue where cancelled point-of-sale refunds in Mexico could still be counted when creating a global invoice. Businesses can now create global invoices correctly when an order has both cancelled and paid refunds, avoiding erroneous negative totals and invoice failures.
Original PR description
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click…
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click "Return Products" to make a refund, but don't pay it, cancel it instead. 4. From the same order, click "Return Products" again to make a second refund, and pay it normally. 5. Go to the orders list, select the main order and the paid refund (not the cancelled one), then Actions > Create Global Invoice. -> Observation: error in the global invoice. In the CFDI tab of the main order the line is "Send Global In Error", and hovering on it the detail says "Failed to distribute some negative lines". Why: ---- When we make the global invoice, we remove the refunds from the order. A cancelled refund was never paid, so we should not count it. But we were counting it too. So we removed the refund amount twice in our case, one for the paid refund, and one for the cancelled one, and we end up with an order with negative amount that cannot be distributed. The fix: -------- We now skip the cancelled orders when we search the refunds, the same way it is done above when we collect the refunded orders. opw-6261404
In ubuntu 26.04 the time zone database contents changed. A couple of timezone names are not available anymore, causing runtime exceptions. This mapping links old with new time zone naming conventions to prevent future (test) breakdown. The mapping WET to Europe/Lisbon is imperfect, and the localizations for the fixed date inside the test did not match. There exists no better nor correct mapping for WET. The mapping to Europe/Lisbon comes from the IANA tzdb-2026c and is official, so it
Original PR description
In ubuntu 26.04 the time zone database contents changed. A couple of timezone names are not available anymore, causing runtime exceptions. This mapping links old with new time zone naming conventions to prevent future (test) breakdown. The mapping WET to Europe/Lisbon is imperfect, and the localizations for the fixed date inside the test did not match. There exists no better nor correct mapping for WET. The mapping to Europe/Lisbon comes from the IANA tzdb-2026c and is official, so it is kept unchanged. The fixed date inside the test is changed to a recent one that aligns the test outcome with expectations: - Offsets match for recent history and future time - Match daylight savings time (DST) observation --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280691
Before this commit, duplicating several projects at once from the list view gave every copy the milestones of all the duplicated projects, because the copy loop assigned the milestones of the whole recordset instead of the ones of the project being copied. Duplicating a single project behaves correctly, which hid the issue. Steps to reproduce: - create two projects with milestones enabled, add a milestone to the first one and two others to the second one - select both projects in the list v
Original PR description
Before this commit, duplicating several projects at once from the list view gave every copy the milestones of all the duplicated projects, because the copy loop assigned the milestones of the whole recordset instead of the ones of the project being copied. Duplicating a single project behaves correctly, which hid the issue. Steps to reproduce: - create two projects with milestones enabled, add a milestone to the first one and two others to the second one - select both projects in the list view and duplicate them Each copy contains the three milestones instead of only the milestones of its original project. Solution: Copy the milestones of the project being duplicated. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
l10n_vn_pos_symbol carries groups='base.group_system,point_of_sale.group_pos_manager', but _prepare_invoice_vals reads it while an ORDINARY salesperson closes an order: _process_saved_order calls _generate_pos_order_invoice as the acting user, with no sudo anywhere on the way. So on a Vietnamese POS with auto-send to SInvoice enabled, a user in point_of_sale.group_pos_user alone hit Access Denied by ACLs for operation: read, model: pos.config, fields: l10n_vn_pos_symbol and the order nev
Original PR description
l10n_vn_pos_symbol carries groups='base.group_system,point_of_sale.group_pos_manager', but _prepare_invoice_vals reads it while an ORDINARY salesperson closes an order: _process_saved_order calls…
l10n_vn_pos_symbol carries groups='base.group_system,point_of_sale.group_pos_manager', but _prepare_invoice_vals reads it while an ORDINARY salesperson closes an order: _process_saved_order calls _generate_pos_order_invoice as the acting user, with no sudo anywhere on the way. So on a Vietnamese POS with auto-send to SInvoice enabled, a user in point_of_sale.group_pos_user alone hit
Access Denied by ACLs for operation: read, model: pos.config, fields: l10n_vn_pos_symbol
and the order never finished syncing - the receipt screen's Done button stayed disabled and the checkout dead-ended.
This is reachable from the module's own test suite, which is what makes it more than theoretical: point_of_sale's start_pos_tour logs in as `pos_user` (group_pos_user, NOT group_pos_manager - that is `pos_admin`), so test_l10n_vn_edi_pos_refund_reason_tour exercises exactly the unprivileged path.
Reading the config as sudo is the right resolution rather than widening the field's groups: the salesperson is never shown the symbol or allowed to change it, it is only stamped onto the invoice their own sale produces, which is what a per-POS configuration field is for. Both symbols are read through the same sudo recordset so the company-level fallback keeps working identically.
Reproduced and verified on v18_full (5-repo addons path):
before - FAILED: [13/36] Tour L10nVnEdiPosRefundReasonTour -> "go to next screen from
receipt"; 1 failed, 0 error(s) of 1 tests
after - tour succeeded for both tours in the class; 0 failed, 0 error(s) of 2 tests,
and no ACL line left in the log
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prSteps to Reproduce the Error (Odoo SaaS 19.2): 1. Install l10n_gcc_invoice localization & Accounting 2. Activate Arabic language (ar_001) and add Default Terms and Conditions in Settings > Configuration > Customer Invoices > Default Terms and Conditions 3. Create invoice with ar_001 partner 4. Confirm the invoice 5. Try to create credit note → Error: KeyError: 'en_US' Root Cause: The _load_narration_translation() workaround reads raw invoice_terms from DB and injects the entire JSONB di
Original PR description
Steps to Reproduce the Error (Odoo SaaS 19.2): 1. Install l10n_gcc_invoice localization & Accounting 2. Activate Arabic language (ar_001) and add Default Terms and Conditions in Settings >…
Steps to Reproduce the Error (Odoo SaaS 19.2):
1. Install l10n_gcc_invoice localization & Accounting
2. Activate Arabic language (ar_001) and add Default Terms and Conditions in Settings > Configuration > Customer Invoices > Default Terms and Conditions
3. Create invoice with ar_001 partner
4. Confirm the invoice
5. Try to create credit note → Error: KeyError: 'en_US'
Root Cause:
The _load_narration_translation() workaround reads raw invoice_terms from DB and injects the entire JSONB dict directly into cache, bypassing ORM field conversion. When Odoo 19.2's improved ORM conversion runs, it creates nested JSON in narration instead of a flat structure.
Timeline:
- bedf1cb66fbb: Workaround added to prevent T&C duplication in preview
- 75f050b9650d: Root cause fixed in report template (conditional display) → Made _load_narration_translation() redundant
- 4e4156536bc9: Odoo 19.2 improved ORM conversion → Now conflicts with the redundant workaround, causing nested JSON
How It Breaks:
1. Invoice creation: _load_narration_translation() injects raw dict into cache
2. ORM writes: nested JSON stored: {ar_001: {en_US: ., ar_001: Arabic}}
3. Credit note creation: copy_translations() expects flat structure → Crashes: KeyError: 'en_US'
Why It's Safe to Remove:
Report template already prevents T&C duplication (commit 75f050b9650d). Removing the workaround restores proper credit note creation without breaking T&C display.
Changes:
- Remove moves._load_narration_translation() in create()
- Remove out self.filtered('id')._load_narration_translation() in _compute_narration()
opw : 6284943
Forward-Port-Of: odoo/odoo#271037### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product: - Invoicing policy = Based on Delivered Quantity - Track service =Timesheets on tasks - Create and confirm a sale order for quantity 1 - Log 20h on the task. Create Invoice → 20.00. Then, post - On the invoice: Reverse → Partial Refund, set the credit-note quantity to 11.00 - Post credit note → qty_invoiced = 9.00 - Log 5h. Create Invoice → 16.00. Post. (this part was already fixed, see
Original PR description
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product: - Invoicing policy = Based on Delivered Quantity - Track service =Timesheets on tasks - Create and confirm…
### Steps to reproduce:
- Download 'Sales' and 'Timesheets' apps
- Create a service product:
- Invoicing policy = Based on Delivered Quantity
- Track service =Timesheets on tasks
- Create and confirm a sale order for quantity 1
- Log 20h on the task. Create Invoice → 20.00. Then, post
- On the invoice: Reverse → Partial Refund, set the credit-note quantity to 11.00
- Post credit note → qty_invoiced = 9.00
- Log 5h. Create Invoice → 16.00. Post. (this part was already fixed, see below)
- Log 3h. Create Invoice
> Expected: an invoice for 3.00h — qty_delivered (28) - qty_invoiced (25)
> Actual: a credit note (out_refund) for 6.00h
### Cause of Issue:
A previous [PR](https://github.com/odoo/odoo/pull/268025) fixed a related issue where `_recompute_qty_to_invoice` failed to account for hours already invoiced/refunded when a credit note had been posted against a prior invoice, causing already-invoiced hours to be re-invoiced.
However, that approach relies on invoice *lineage* (which invoice was reversed) rather than on the actual net amount already invoiced.
https://github.com/odoo/odoo/blob/427d398880c381981e088f7001f855a10d1cd581/addons/sale_timesheet/models/sale_order_line.py#L183 This caused the already-resolved refund to be subtracted a second time (3-(20-11) = -6), producing a negative `qty_to_invoice` and, consequently, a credit note instead of an invoice for the newly logged hours.
### Fix:
Replace the lineage-based tracking with a simpler and more efficient way that holds across any number of refund/re-invoice cycles.
In case there are refund moves:
`qty_to_invoice = total_delivered_quantity − net_posted_invoiced_quantity`
`net_posted_invoiced_quantity` is computed directly from all **posted** invoice lines linked to the
SO line, without needing to determine which invoice a given credit note was reversing or whether
its timesheets have since moved to a newer invoice.
And in case of no refunds (less complex flow), we only include the relevant timesheets.
opw-6253650When modifying project_id on a timesheet through mass edit/rpc or anything that is not triggering `onChange`. The task_id would not be reset if it doesnt' belong to the new project set on the timesheet. Steps to reproduce: ------------------- * Install studio for easier reproducing of the issue * Open the timesheet list view * Open studio and activate the mass edit on the view * Modify the project_id on multiple records > Observation: The task_id stays the same even if they do not belon
Original PR description
When modifying project_id on a timesheet through mass edit/rpc or anything that is not triggering `onChange`. The task_id would not be reset if it doesnt' belong to the new project set on the timesheet. Steps to reproduce: ------------------- * Install studio for easier reproducing of the issue * Open the timesheet list view * Open studio and activate the mass edit on the view * Modify the project_id on multiple records > Observation: The task_id stays the same even if they do not belong to the new set project Why the fix: ------------ Instead of relying only on the onChange we add an inverse to the project_id that will reset the task when needed. opw-6259149
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the re-derivation runs while the lines are no longer protected by the invoice link. When the task or project no longer resolves to a sale order item (e.g. it was unlinked after invoicing), the timesheets lose their sale order item or get reassigned to another one, so the delivered hours silently disappear
Original PR description
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the…
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the re-derivation runs while the lines are no longer protected by the invoice link. When the task or project no longer resolves to a sale order item (e.g. it was unlinked after invoicing), the timesheets lose their sale order item or get reassigned to another one, so the delivered hours silently disappear from the original order line. Protect so_line during the write and drop the pending recompute: deleting an invoice must only make the hours invoiceable again, not change their allocation. Steps to reproduce: - Install Sales and Timesheets - Create a service product with invoice policy "Based on Timesheets" and "Create a task in a new project" - Create and confirm a sale order with this product - Log a timesheet on the generated task - Create the invoice (keep it in draft) - Remove the Sales Order Item from the task and from the project settings (or point them to a sale order item of another order) - Delete the draft invoice - Open the timesheet: its Sales Order Item is emptied (or replaced by the other order's item, whose delivered quantity now includes the hours sold on the original order), and the original line's delivered quantity is reset --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279552
Prerequisite: ------------------------------------- 1. Configure outgoing mail server Steps to reproduce: ------------------------------------- 1. Install Contact and Sales modules 2. Create a Company contact and it's child contact as follows: * Type: Invoice Address * Keep the Name field empty and add any email 3. Create and confirm the sale order with the Child Contact as customer 4. Send the order by Email 5. Click on the mail icon on the Mail in Chatter Observation:
Original PR description
Prerequisite: ------------------------------------- 1. Configure outgoing mail server Steps to reproduce: ------------------------------------- 1. Install Contact and Sales modules 2. Create a…
Prerequisite:
-------------------------------------
1. Configure outgoing mail server
Steps to reproduce:
-------------------------------------
1. Install Contact and Sales modules
2. Create a Company contact and it's child contact as follows:
* Type: Invoice Address
* Keep the Name field empty and add any email
3. Create and confirm the sale order with the Child Contact as customer
4. Send the order by Email
5. Click on the mail icon on the Mail in Chatter
Observation:
-------------------------------------
The recipient name is displayed as false in the notification popover.
Issue:
-------------------------------------
The bug traces through two separate layers
1. Child contacts of type invoice, delivery, etc. are allowed to have `name = False`. `_to_store()` serialized only the name field for recipients, which produced `{'name': false}` for nameless contacts.
2. The frontend used `name` with no fallback and rendered 'false' https://github.com/odoo/odoo/blob/0b17840fb3cc72935e1a6302a057fb55c253c498/addons/mail/static/src/core/common/message_notification_popover.xml#L8
Solution:
-------------------------------------
1. Include `display_name` in the recipient serialization, since it is always computed and never empty
2. Use `displayName` as a frontend fallback when name is missing.
3. Used `sudo` in a condition to respect the multicompany access rights
**Before:**
<img width="576" height="314" alt="image" src="https://github.com/user-attachments/assets/26c7e67d-9ab2-4089-bd07-2871c2039f8f" />
**After:**
<img width="630" height="317" alt="image" src="https://github.com/user-attachments/assets/7c84afef-48b3-4243-bf6e-3bac7198ccf6" />
opw-5898004The right value often comes from the user and the search implementation may not handle 'any' properly, however 'in' is nearly always implemented. For most cases, transform 'any' into a 'in' before calling Field.search. task-6446206 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280362
Original PR description
The right value often comes from the user and the search implementation may not handle 'any' properly, however 'in' is nearly always implemented. For most cases, transform 'any' into a 'in' before calling Field.search. task-6446206 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280362
### Steps to reproduce: - Go to Email Marketing > Mailing Lists > Mailing Lists - Open any mailing list and click the 'Recipients' smart button > Only the contacts belonging to that mailing list are shown, as expected - Refresh the page > All mailing contacts in the database are shown instead, filtered only by `('is_blacklisted', '=', False)`. Notice that the mailing list's id is still present in the URL, but is no longer reflected in the results ### Cause of Issue: The 'Recipien
Original PR description
### Steps to reproduce: - Go to Email Marketing > Mailing Lists > Mailing Lists - Open any mailing list and click the 'Recipients' smart button > Only the contacts belonging to that mailing list are…
### Steps to reproduce:
- Go to Email Marketing > Mailing Lists > Mailing Lists
- Open any mailing list and click the 'Recipients' smart button
> Only the contacts belonging to that mailing list are shown, as expected
- Refresh the page
> All mailing contacts in the database are shown instead, filtered only by
`('is_blacklisted', '=', False)`. Notice that the mailing list's id is still present in the URL, but is no longer reflected in the results
### Cause of Issue:
The 'Recipients' button called `action_view_contacts()`, which fetched the *generic* action also used by the 'Mailing List Contacts' menu (`mass_mailing.action_view_mass_mailing_contacts`) and patched its domain and context in memory before returning it: https://github.com/odoo/odoo/blob/76e005f57ffc5aca296d261a382b74ff95bd5990/addons/mass_mailing/models/mailing_list.py#L168-L172
This override only exists for the response triggered by the button click. The generic action's own `domain` field, stored in `ir.actions.act_window`, is empty and evaluates to [], while the `context` is `{'search_default_filter_not_email_bl': 1}`
https://github.com/odoo/odoo/blob/76e005f57ffc5aca296d261a382b74ff95bd5990/addons/mass_mailing/views/mailing_contact_views.xml#L165-L177 This filter has the domain `["is_blacklisted", "=", false]` https://github.com/odoo/odoo/blob/76e005f57ffc5aca296d261a382b74ff95bd5990/addons/mass_mailing/views/mailing_contact_views.xml#L23-L26
On refresh, the web client rebuilds the current action purely from the action id encoded in the URL. It re-reads the action definition straight from `ir.actions.act_window` and never re-invokes `action_view_contacts()`, so the in-memory domain override from the original click is lost and only the generic action's static domain remains.
### Fix:
Gave the 'Mailing List Contacts' menu its own dedicated action (`action_view_mailing_contacts_menu`)
and adjusted the old view to suit the 'Recepients' instead of reusing the generic one so that the
filter will live on the action record itself and will be evaluated server-side from `active_id` on
every load. Also, this way there won't be any traceback on existing DBs if they get restarted
without upgrading their xml actions, since all the changes are in .xml files.
opw-6385589