details: https://code.tryton.org/tryton/commit/bf2f5a9b28e2
branch: default
user: Cédric Krier <[email protected]>
date: Wed Oct 28 17:49:25 2026 +0100
description:
Do not use the search window when deleting/removing non-stored records
from One2Many
The search window relies on a ID-based domain. Therefore, non-stored
records
cannot be displayed in the search, which prevents them from being
deleted/removed.
Closes #15199
diffstat:
sao/src/view/form.js | 13 ++++++++--
tryton/tryton/gui/window/view_form/view/form_gtk/one2many.py | 15 ++++++++----
2 files changed, 20 insertions(+), 8 deletions(-)
diffs (82 lines):
diff -r 8bbb83c3f4d6 -r bf2f5a9b28e2 sao/src/view/form.js
--- a/sao/src/view/form.js Fri Oct 30 09:31:43 2026 +0100
+++ b/sao/src/view/form.js Wed Oct 28 17:49:25 2026 +0100
@@ -3585,9 +3585,13 @@
first = this._position <= 1;
last = this._position >= this._length;
}
+ let has_selection = Boolean(this.screen.selected_records.length);
+ let searchable = this.screen.listed_records.every(
+ record => record.id >= 0);
var deletable =
this.screen.deletable &&
- this.screen.group.some((r) => !r.deleted && !r.removed);
+ this.screen.group.some((r) => !r.deleted && !r.removed) &&
+ (has_selection || searchable);
var undeletable =
this.screen.selected_records.some((r) => r.deleted ||
r.removed);
const view_type = this.screen.current_view.view_type;
@@ -3637,7 +3641,8 @@
first);
if (this.attributes.add_remove) {
let removable =
- this.screen.group.some((r) => !r.deleted && !r.removed);
+ this.screen.group.some((r) => !r.deleted && !r.removed) &&
+ (has_selection || searchable);
this.but_add.prop(
'disabled',
this._readonly ||
@@ -3807,7 +3812,9 @@
},
_remove: function(remove=false) {
let selected_records = this.screen.selected_records;
- if (selected_records.length <= 0 || selected_records.length >= 2) {
+ let listed_records = this.screen.listed_records;
+ if ((selected_records.length <= 0 || selected_records.length >= 2)
&&
+ (listed_records.every(record => record.id >= 0))) {
let domain = [['id', 'in', this.field.get_eval(this.record)]];
let context = this.field.get_search_context(this.record);
let order = this.field.description.order;
diff -r 8bbb83c3f4d6 -r bf2f5a9b28e2
tryton/tryton/gui/window/view_form/view/form_gtk/one2many.py
--- a/tryton/tryton/gui/window/view_form/view/form_gtk/one2many.py Fri Oct
30 09:31:43 2026 +0100
+++ b/tryton/tryton/gui/window/view_form/view/form_gtk/one2many.py Wed Oct
28 17:49:25 2026 +0100
@@ -290,11 +290,14 @@
if isinstance(self._position, int):
first = self._position <= 1
last = self._position >= self._length
+ has_selection = bool(self.screen.selected_records)
+ searchable = all(r.id >= 0 for r in self.screen.listed_records)
deletable = (
self.screen.deletable
and any(
not r.deleted and not r.removed
- for r in self.screen.group))
+ for r in self.screen.group)
+ and (has_selection or searchable))
undeletable = any(
r.deleted or r.removed for r in self.screen.selected_records)
view_type = self.screen.current_view.view_type
@@ -327,9 +330,10 @@
self._length
and not first))
if self.attrs.get('add_remove'):
- removable = any(
- not r.deleted and not r.removed
- for r in self.screen.group)
+ removable = (any(
+ not r.deleted and not r.removed
+ for r in self.screen.group)
+ and (has_selection or searchable))
self.but_add.set_sensitive(bool(
not self._readonly
and not size_limit
@@ -506,7 +510,8 @@
else:
if not self.delete_access or not deletable:
return
- if not (0 < len(self.screen.selected_records) < 20):
+ if (not (0 < len(self.screen.selected_records) < 20)
+ and all(r.id >= 0 for r in self.screen.listed_records)):
domain = [('id', 'in', self.field.get_eval(self.record))]
context = self.field.get_search_context(self.record)
order = self.field.attrs.get('order')