From 2ed47d698f3d8dcb05c3bd779f0271060e335b3a Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Sat, 16 May 2026 09:15:21 +0200 Subject: [PATCH] Bug search --- AG_GRID_PROGRESS.md | 9 + AG_GRID_SESSION_INDEX.md | 7 + dist/tryton-sao.js | 5 + tryton-sao.js | 5 + tryton-sao_.js | 28517 ------------------------------------- 5 files changed, 26 insertions(+), 28517 deletions(-) delete mode 100644 tryton-sao_.js diff --git a/AG_GRID_PROGRESS.md b/AG_GRID_PROGRESS.md index e8a5c41..15dcb13 100644 --- a/AG_GRID_PROGRESS.md +++ b/AG_GRID_PROGRESS.md @@ -792,6 +792,15 @@ Point de deploiement: - comme `index.html` est dans le repo parent cote serveur, verifier que le fichier `images/tradon-icon.png` est bien servi au bon chemin relatif - le cache favicon navigateur peut etre tenace: refresh dur ou vidage cache si besoin +### Correctif recherche Many2One / modales de selection + +- probleme observe: depuis la nouvelle AG Grid, les fenetres `Search ...` ouvertes par la loupe des champs `Many2One` affichaient une grille vide +- exemple: `Search Broker` depuis un contrat, alors que des `party.party` de categorie Broker existent bien +- cause: `Sao.Window.Search` creait une vue `tree` readonly sans marqueur de contexte, donc le diagnostic AG Grid la prenait pour une liste principale plate +- correctif: les ecrans crees par `Sao.Window.Search` portent maintenant `search_window: true` +- `Sao.View.AGGridTree.get_support_diagnostics` exclut ce contexte avec `fallbackReason: search_window` +- resultat attendu: les recherches `Many2One` repassent sur le tree historique, avec selection simple/multiple et `search_filter` SAO standard + ### Commits importants - `fc9ab5d Save SAO AG Grid work` diff --git a/AG_GRID_SESSION_INDEX.md b/AG_GRID_SESSION_INDEX.md index 287a008..55792ab 100644 --- a/AG_GRID_SESSION_INDEX.md +++ b/AG_GRID_SESSION_INDEX.md @@ -233,3 +233,10 @@ Traces a lire si le sujet revient: - retirer les traces temporaires une fois la stabilisation confirmee - garder le reflexe des traces JSON plates pour les bugs AG Grid difficiles + +### Dernier correctif en cours + +- les modales `Sao.Window.Search` ouvertes par les loupes `Many2One` ne doivent pas utiliser AG Grid pour l'instant +- marqueur ajoute: `search_window: true` +- fallback AG Grid ajoute: `search_window` +- symptome corrige: fenetre `Search Broker` vide alors que les records existent diff --git a/dist/tryton-sao.js b/dist/tryton-sao.js index 0182585..ed3f613 100644 --- a/dist/tryton-sao.js +++ b/dist/tryton-sao.js @@ -24295,6 +24295,8 @@ function eval_pyson(value){ preValidate: Boolean( screen && (screen.pre_validate || (screen.attributes && screen.attributes.pre_validate))), + searchWindow: Boolean( + screen && screen.attributes && screen.attributes.search_window), hasParentGroup: Boolean( screen && screen.group && screen.group.parent), childName: screen && screen.group && screen.group.child_name || null, @@ -24324,6 +24326,8 @@ function eval_pyson(value){ diagnostics.fallbackReason = 'children_field'; } else if (!diagnostics.agGridLoaded) { diagnostics.fallbackReason = 'ag_grid_not_loaded'; + } else if (diagnostics.searchWindow) { + diagnostics.fallbackReason = 'search_window'; } else if (diagnostics.embeddedContext) { diagnostics.fallbackReason = 'embedded_relation_tree'; } else if (diagnostics.editable) { @@ -29785,6 +29789,7 @@ function eval_pyson(value){ views_preload: views_preload, row_activate: this.activate.bind(this), readonly: true, + search_window: true, breadcrumb: [this.title], }); this.screen.load_next_view().done(() => { diff --git a/tryton-sao.js b/tryton-sao.js index 0182585..ed3f613 100644 --- a/tryton-sao.js +++ b/tryton-sao.js @@ -24295,6 +24295,8 @@ function eval_pyson(value){ preValidate: Boolean( screen && (screen.pre_validate || (screen.attributes && screen.attributes.pre_validate))), + searchWindow: Boolean( + screen && screen.attributes && screen.attributes.search_window), hasParentGroup: Boolean( screen && screen.group && screen.group.parent), childName: screen && screen.group && screen.group.child_name || null, @@ -24324,6 +24326,8 @@ function eval_pyson(value){ diagnostics.fallbackReason = 'children_field'; } else if (!diagnostics.agGridLoaded) { diagnostics.fallbackReason = 'ag_grid_not_loaded'; + } else if (diagnostics.searchWindow) { + diagnostics.fallbackReason = 'search_window'; } else if (diagnostics.embeddedContext) { diagnostics.fallbackReason = 'embedded_relation_tree'; } else if (diagnostics.editable) { @@ -29785,6 +29789,7 @@ function eval_pyson(value){ views_preload: views_preload, row_activate: this.activate.bind(this), readonly: true, + search_window: true, breadcrumb: [this.title], }); this.screen.load_next_view().done(() => { diff --git a/tryton-sao_.js b/tryton-sao_.js deleted file mode 100644 index 0a0d7f7..0000000 --- a/tryton-sao_.js +++ /dev/null @@ -1,28517 +0,0 @@ -/* This file is part of Tryton. The COPYRIGHT file at the top level of - this repository contains the full copyright notices and license terms. */ - -/* eslint-disable no-redeclare */ -var Sao = { - __version__: '7.2.5', -}; -/* eslint-enable no-redeclare */ - -(function() { - 'use strict'; - - // Browser compatibility: polyfill - if (!('contains' in String.prototype)) { - String.prototype.contains = function(str, startIndex) { - return -1 !== String.prototype.indexOf.call(this, str, startIndex); - }; - } - if (!String.prototype.startsWith) { - Object.defineProperty(String.prototype, 'startsWith', { - enumerable: false, - configurable: false, - writable: false, - value: function(searchString, position) { - position = position || 0; - return this.indexOf(searchString, position) === position; - } - }); - } - if (!String.prototype.endsWith) { - Object.defineProperty(String.prototype, 'endsWith', { - enumerable: false, - configurable: false, - writable: false, - value: function(searchString, position) { - position = position || this.length; - position = position - searchString.length; - var lastIndex = this.lastIndexOf(searchString); - return lastIndex !== -1 && lastIndex === position; - } - }); - } - if (!String.prototype.padEnd) { - String.prototype.padEnd = function padEnd(targetLength, padString) { - targetLength = targetLength >> 0; - padString = String( - typeof padString !== 'undefined' ? padString : ' '); - if (this.length > targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat( - targetLength / padString.length); - } - return String(this) + padString.slice(0, targetLength); - } - }; - } - if (!String.prototype.padStart) { - String.prototype.padStart = function padStart(targetLength, padString) { - targetLength = targetLength >> 0; - padString = String( - typeof padString !== 'undefined' ? padString : ' '); - if (this.length > targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat( - targetLength / padString.length); - } - return padString.slice(0, targetLength) + String(this); - } - }; - } - if (!Array.prototype.some) { - Array.prototype.some = function(fun /*, thisp */) { - if (this === null) { - throw new TypeError(); - } - var thisp, i, - t = Object(this), - len = t.length >>> 0; - if (typeof fun !== 'function') { - throw new TypeError(); - } - thisp = arguments[1]; - for (i = 0; i < len; i++) { - if (i in t && fun.call(thisp, t[i], i, t)) { - return true; - } - } - return false; - }; - } - - if (!Array.from) { - Array.from = function (value) { - // Implementation is not strictly equivalent but works for most - // cases - var result = []; - for (const e of value) { - result.push(e); - } - return result; - }; - } - - if (!Set.prototype.intersection) { - Set.prototype.intersection = function(other) { - if (this === null) { - throw new TypeError(); - } - const result = new Set(); - for (const key of other.keys()) { - if (this.has(key)) { - result.add(key) - } - } - return result; - } - } - - if (!Set.prototype.isSubsetOf) { - Set.prototype.isSubsetOf = function(other) { - if (this === null) { - throw new TypeError(); - } - for (const key of this.keys()) { - if (!other.has(key)) { - return false; - } - } - return true; - } - } - - Sao.setdefault = function(object, key, value) { - if (!Object.prototype.hasOwnProperty.call(object, key)) { - object[key] = value; - } - return object[key]; - }; - - // Ensure RichText doesn't use style with css - try { - document.execCommand('styleWithCSS', false, false); - } catch (e) { - // continue - } - try { - document.execCommand('useCSS', false, true); - } catch (e) { - // continue - } - - // Add .uniqueId to jQuery - jQuery.fn.extend({ - uniqueId: (function() { - var uuid = 0; - return function() { - return this.each(function() { - if (!this.id) { - this.id = "ui-id-" + (++uuid); - } - }); - }; - })() - }); - - window.onbeforeunload = function(e) { - if (Sao.main_menu_screen) { - Sao.main_menu_screen.save_tree_state(true); - } - if (Sao.Tab.tabs.length) { - var dialog = Sao.i18n.gettext("Are your sure to leave?"); - e.returnValue = dialog; - return dialog; - } - }; - - Sao.class_ = function(Parent, props) { - var ClassConstructor = function() { - if (!(this instanceof ClassConstructor)) - throw new Error('Constructor function requires new operator'); - this.Class = ClassConstructor; - if (this.init) { - this.init.apply(this, arguments); - } - }; - - // Plug prototype chain - ClassConstructor.prototype = Object.create(Parent.prototype); - ClassConstructor._super = Parent.prototype; - if (props) { - for (var name in props) { - Object.defineProperty(ClassConstructor.prototype, name, - Object.getOwnPropertyDescriptor(props, name)); - } - } - - // Method to create new instance with a list of arguments - function F(args) { - return ClassConstructor.apply(this, args); - } - F.prototype = ClassConstructor.prototype; - ClassConstructor.new_ = function(args) { - return new F(args); - }; - return ClassConstructor; - }; - - Sao.Logging = Sao.class_(Object, { - init: function() { - this.level = Sao.Logging.ERROR; - }, - set_level: function(level) { - this.level = level; - }, - _log: function(level, logger, args) { - if (this.level <= level) { - logger.apply(console, args); - } - }, - debug: function() { - this._log(Sao.Logging.DEBUG, console.log, arguments); - }, - info: function() { - this._log(Sao.Logging.INFO, console.info, arguments); - }, - warn: function() { - this._log(Sao.Logging.WARNING, console.warn, arguments); - }, - error: function() { - this._log(Sao.Logging.ERROR, console.error, arguments); - }, - critical: function() { - this._log(Sao.Logging.CRITICAL, console.error, arguments); - }, - assert: function() { - if (this.level <= Sao.Logging.DEBUG) { - console.assert.apply(console, arguments); - } - }, - }); - Sao.Logging.CRITICAL = 50; - Sao.Logging.ERROR = 40; - Sao.Logging.WARNING = 30; - Sao.Logging.INFO = 20; - Sao.Logging.DEBUG = 10; - Sao.Logging.NOTSET = 0; - Sao.Logger = new Sao.Logging(); - - Sao.Decimal = Number; - - var _moment_to_string = moment.prototype.toString; - moment.prototype.toString = function() { - if (this.isDate) { - return this.format('YYYY-MM-DD'); - } else if (this.isDateTime) { - if (this.milliseconds()) { - return this.format('YYYY-MM-DD HH:mm:ss.SSSSSS'); - } else { - return this.format('YYYY-MM-DD HH:mm:ss'); - } - } else if (this.isTime) { - if (this.milliseconds()) { - return this.format('HH:mm:ss.SSSSSS'); - } else { - return this.format('HH:mm:ss'); - } - } else { - return _moment_to_string.call(this); - } - }; - - Sao.Date = function(year, month, day) { - var date; - if (month === undefined) { - date = moment(year); - year = undefined; - } - else { - date = moment(); - } - date.year(year); - date.month(month); - date.date(day); - date.set({hour: 0, minute: 0, second: 0, millisecond: 0}); - date.isDate = true; - return date; - }; - - // Add 1 day to the limit because setting time make it out of the range - Sao.Date.min = moment(new Date((-100000000 + 1) * 86400000)); - Sao.Date.min.set({hour: 0, minute: 0, second: 0, millisecond: 0}); - Sao.Date.min.isDate = true; - Sao.Date.max = moment(new Date(100000000 * 86400000)); - Sao.Date.max.set({hour: 0, minute: 0, second: 0, millisecond: 0}); - Sao.Date.max.isDate = true; - - Sao.DateTime = function( - year, month, day, - hour=0, minute=0, second=0, millisecond=0, utc=false) { - var datetime; - if (month === undefined) { - datetime = moment(year); - year = undefined; - } - else { - datetime = moment(); - } - if (utc) { - datetime.utc(); - } - datetime.year(year); - datetime.month(month); - datetime.date(day); - if (month !== undefined) { - datetime.hour(hour); - datetime.minute(minute); - datetime.second(second); - datetime.milliseconds(millisecond); - } - datetime.isDateTime = true; - datetime.local(); - datetime.todate = function() { - return Sao.Date(this.year(), this.month(), this.date()); - }; - datetime.totime = function() { - return Sao.Time( - this.hour(), this.minute(), this.second(), this.millisecond()); - }; - return datetime; - }; - - Sao.DateTime.combine = function(date, time) { - return Sao.DateTime( - date.year(), date.month(), date.date(), - time.hour(), time.minute(), time.second(), time.millisecond()); - }; - - Sao.DateTime.min = moment(new Date(-100000000 * 86400000)).local(); - Sao.DateTime.min.isDateTime = true; - Sao.DateTime.max = moment(new Date(100000000 * 86400000)).local(); - Sao.DateTime.max.isDateTime = true; - - Sao.Time = function(hour, minute, second, millisecond) { - var time = moment({hour: hour, minute: minute, second: second, - millisecond: millisecond || 0}); - time.isTime = true; - return time; - }; - - Sao.TimeDelta = function(days, seconds, - milliseconds, minutes, hours, weeks) { - var timedelta = moment.duration({ - days: days, - seconds: seconds, - milliseconds: milliseconds, - minutes: minutes, - hours: hours, - weeks: weeks - }); - timedelta.isTimeDelta = true; - return timedelta; - }; - - Sao.config = {}; - Sao.config.limit = 1000; - Sao.config.display_size = 20; - var doc_version = Sao.__version__.split('.').slice(0, 2); - if (parseInt(doc_version[1], 10) % 2) { - doc_version = 'latest'; - } else { - doc_version = doc_version.join('.'); - } - Sao.config.doc_url = `https://docs.tryton.org/${doc_version}`; - Sao.config.bug_url = 'https://bugs.tryton.org/'; - Sao.config.title = 'Tryton'; - Sao.config.icon_colors = '#267f82,#3e4950,#e78e42'.split(','); - Sao.config.calendar_colors = '#fff,#267f82'.split(','); - Sao.config.graph_color = '#267f82'; - Sao.config.bus_timeout = 10 * 60 * 1000; - Sao.config.image_max_size = Math.pow(10, 6); - - Sao.i18n = i18n(); - Sao.i18n.setlang = function(lang) { - if (!lang) { - lang = (navigator.language || - navigator.browserLanguage || - navigator.userLanguage || - 'en').replace('-', '_'); - } - jQuery('html').attr('lang', lang); - Sao.i18n.setLocale(lang); - moment.locale(lang.slice(0, 2)); - return jQuery.getJSON('locale/' + lang + '.json').then(function(data) { - if (!data[''].language) { - data[''].language = lang; - } - if (!data['']['plural-forms']) { - data['']['plural-forms'] = 'nplurals=2; plural=(n!=1);'; - } - // gettext.js requires to dump untranslated keys - for (var key in data) { - if ('' === key) { - continue; - } - data[key] = 2 == data[key].length ? data[key][1] : data[key].slice(1); - } - Sao.i18n.loadJSON(data); - }, function() { - if (~lang.indexOf('_')) { - return Sao.i18n.setlang(lang.split('_').slice(0, -1).join('_')); - } - }); - }; - Sao.i18n.getlang = function() { - return Sao.i18n.getLocale(); - }; - Sao.i18n.BC47 = function(lang) { - return lang.replace('_', '-'); - }; - Sao.i18n.set_direction = function(direction) { - Sao.i18n.rtl = (direction === 'rtl'); - jQuery('html').attr('dir', direction); - jQuery('.row-offcanvas') - .removeClass('row-offcanvas-left row-offcanvas-right') - .addClass(Sao.i18n.rtl ? 'row-offcanvas-right' : 'row-offcanvas-left'); - }; - Sao.i18n.locale = {}; - - Sao.BOM_UTF8 = '\uFEFF'; - - Sao.get_preferences = function() { - var session = Sao.Session.current_session; - return session.reload_context().then(function() { - return Sao.rpc({ - 'method': 'model.res.user.get_preferences', - 'params': [false, {}] - }, session).then(function(preferences) { - var deferreds = []; - deferreds.push(Sao.common.MODELACCESS.load_models()); - deferreds.push(Sao.common.MODELHISTORY.load_history()); - deferreds.push(Sao.common.MODELNOTIFICATION.load_names()); - deferreds.push(Sao.common.VIEW_SEARCH.load_searches()); - return jQuery.when.apply(jQuery, deferreds).then(function() { - for (const action_id of (preferences.actions || [])) { - Sao.Action.execute(action_id, {}, null, {}); - } - Sao.set_title(); - var new_lang = preferences.language != Sao.i18n.getLocale(); - var prm = jQuery.Deferred(); - Sao.i18n.setlang(preferences.language).always(function() { - if (new_lang) { - Sao.user_menu(preferences); - } - prm.resolve(preferences); - }); - Sao.i18n.set_direction(preferences.language_direction); - Sao.i18n.locale = preferences.locale; - Sao.common.MODELNAME.clear(); - return prm; - }); - }); - }); - }; - - Sao.set_title = function(name) { - var title = [name, Sao.config.title]; - document.title = title.filter(function(e) {return e;}).join(' - '); - jQuery('#title').text(Sao.config.title); - }; - - Sao.set_url = function(path, name) { - var session = Sao.Session.current_session; - if (session) { - var url = '#' + session.database; - if (path) { - url += '/' + path; - } - window.location = url; - } - Sao.set_title(name); - }; - - window.onhashchange = function() { - var session = Sao.Session.current_session; - if (!session) { - return; - } - var url, - database = '#' + session.database; - if (window.location.hash == database) { - url = ''; - } else if (window.location.hash.startsWith(database + '/')) { - url = window.location.hash.substr(database.length + 1); - } else { - return; - } - var tab; - if (!url) { - tab = Sao.Tab.tabs.get_current(); - if (tab) { - Sao.set_url(tab.get_url(), tab.name); - } - } else { - url = decodeURIComponent(url); - for (const tab of Sao.Tab.tabs) { - if (decodeURIComponent(tab.get_url()) == url) { - tab.show(); - return; - } - } - Sao.open_url(); - } - }; - - Sao.open_url = function(url) { - function loads(value) { - return Sao.rpc.convertJSONObject(jQuery.parseJSON(value)); - } - if (url === undefined) { - url = window.location.hash.substr(1); - } - var i = url.indexOf(';'); - var path, params = {}; - if (i >= 0) { - path = url.substring(0, i); - for (const part of url.substring(i + 1).split('&')) { - if (part) { - var item = part.split('=').map(decodeURIComponent); - params[item[0]] = item[1]; - } - } - } else { - path = url; - } - path = path.split('/').slice(1); - var type = path.shift(); - - function open_model(path) { - var attributes = {}; - attributes.model = path.shift(); - if (!attributes.model) { - return; - } - try { - attributes.view_ids = loads(params.views || '[]'); - if (params.limit !== undefined) { - attributes.limit = loads(params.limit || 'null'); - } - attributes.name = loads(params.name || '""'); - attributes.search_value = loads(params.search_value || '[]'); - attributes.domain = loads(params.domain || '[]'); - attributes.context = loads(params.context || '{}'); - attributes.context_model = params.context_model; - attributes.tab_domain = loads(params.tab_domain || '[]'); - } catch (e) { - return; - } - var res_id = path.shift(); - if (res_id) { - res_id = Number(res_id); - if (isNaN(res_id)) { - return; - } - attributes.res_id = res_id; - attributes.mode = ['form', 'tree']; - } - try { - Sao.Tab.create(attributes); - } catch (e) { - // Prevent crashing the client - return; - } - } - function open_wizard(path) { - var attributes = {}; - attributes.action = path[0]; - if (!attributes.action) { - return; - } - try { - attributes.data = loads(params.data || '{}'); - attributes.direct_print = loads(params.direct_print || 'false'); - attributes.name = loads(params.name || '""'); - attributes.window = loads(params.window || 'false'); - attributes.context = loads(params.context || '{}'); - } catch (e) { - return; - } - try { - Sao.Wizard.create(attributes); - } catch (e) { - // Prevent crashing the client - return; - } - } - function open_report(path) { - var attributes = {}; - attributes.name = path[0]; - if (!attributes.name) { - return; - } - try { - attributes.data = loads(params.data || '{}'); - attributes.direct_print = loads(params.direct_print || 'false'); - attributes.context = loads(params.context || '{}'); - } catch (e) { - return; - } - try { - Sao.Action.exec_report(attributes); - } catch (e) { - // Prevent crashing the client - return; - } - } - function open_url() { - var url; - try { - url = loads(params.url || 'false'); - } catch (e) { - return; - } - if (url) { - window.open(url, '_blank', 'noreferrer,noopener'); - } - } - - switch (type) { - case 'model': - open_model(path); - break; - case 'wizard': - open_wizard(path); - break; - case 'report': - open_report(path); - break; - case 'url': - open_url(); - break; - } - }; - - Sao.login = function() { - Sao.set_title(); - Sao.i18n.setlang().always(function() { - Sao.Session.server_version() - .then(function(version) { - if (JSON.stringify(version.split('.').slice(0, 2)) !== - JSON.stringify(Sao.__version__.split('.').slice(0, 2))) - { - Sao.common.warning.run( - Sao.i18n.gettext( - "Incompatible version of the server."), - Sao.i18n.gettext("Version mismatch")); - } else { - Sao.Session.get_credentials() - .then(function(session) { - Sao.Session.current_session = session; - return session.reload_context(); - }) - .then(Sao.get_preferences) - .then(function(preferences) { - Sao.menu(preferences); - Sao.user_menu(preferences); - Sao.open_url(); - Sao.Bus.listen(); - }); - } - }, function() { - Sao.common.warning.run( - Sao.i18n.gettext( - "Could not connect to the server."), - Sao.i18n.gettext("Connection error")); - }); - }); - }; - - Sao.logout = function() { - var session = Sao.Session.current_session; - if (!session || !session.session) { - // Do not save states if there is no session - Sao.main_menu_screen = null; - } - Sao.Tab.tabs.close(true).done(function() { - jQuery('#user-preferences').empty(); - jQuery('#global-search').empty(); - jQuery('#menu').empty(); - session.do_logout().always(Sao.login); - Sao.set_title(); - }); - }; - - Sao.preferences = function() { - return Sao.Tab.tabs.close(true).then(function() { - jQuery('#user-preferences').empty(); - jQuery('#global-search').empty(); - jQuery('#menu').empty(); - new Sao.Window.Preferences(function() { - Sao.Session.current_session.reset_context(); - Sao.get_preferences().then(function(preferences) { - Sao.menu(preferences); - Sao.user_menu(preferences); - }); - }); - }); - }; - Sao.favorites_menu = function() { - jQuery(window).click(function() { - Sao.favorites_menu_clear(); - }); - if (!jQuery('#user-favorites').children('.dropdown-menu').length) { - var name = Sao.main_menu_screen.model_name + '.favorite'; - var session = Sao.Session.current_session; - var args = { - 'method': 'model.' + name + '.get', - }; - var menu = jQuery('