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')

Reply via email to