Merge pull request #4792 from expsa/eltayar

enhance_search
This commit is contained in:
Mohamed Eltayar 2025-10-01 16:49:59 +03:00 committed by GitHub
commit ab0106ce19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 134 additions and 44 deletions

View File

@ -125,6 +125,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
self._customSearchState.value = value; self._customSearchState.value = value;
return self.update({}, {reload: false}); return self.update({}, {reload: false});
}).catch(function(error) { }).catch(function(error) {
console.error('FIMS Search Error:', error);
self._showSearchError(); self._showSearchError();
return Promise.resolve(); return Promise.resolve();
}).finally(function() { }).finally(function() {
@ -177,11 +178,19 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
}); });
}, },
/**
* بناء domain للبحث - مع دعم non-stored fields
* @param {String} value - قيمة البحث
* @returns {Array} domain صحيح دائماً
*/
_buildCustomSearchDomain: function(value) { _buildCustomSearchDomain: function(value) {
if (!value) return []; if (!value) return [];
var fields = this._getCustomSearchableFields(); var fields = this._getCustomSearchableFields();
if (!fields || fields.length === 0) return []; if (!fields || fields.length === 0) {
console.warn('FIMS Search: No searchable fields found');
return [];
}
var conditions = []; var conditions = [];
var normalized = this._normalizeText(value); var normalized = this._normalizeText(value);
@ -193,47 +202,70 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
fields.forEach(function(field) { fields.forEach(function(field) {
var fieldType = field.type; var fieldType = field.type;
var fieldName = field.name; var fieldName = field.name;
var fieldPath = field.searchPath || fieldName; // دعم related fields
switch(fieldType) { switch(fieldType) {
case 'char': case 'char':
case 'text': case 'text':
case 'html': case 'html':
case 'many2one':
case 'selection':
searchValues.forEach(function(searchVal) { searchValues.forEach(function(searchVal) {
conditions.push([fieldName, 'ilike', searchVal]); conditions.push([fieldPath, 'ilike', searchVal]);
}); });
break; break;
case 'many2one':
// البحث في اسم الـ relation
searchValues.forEach(function(searchVal) {
conditions.push([fieldPath, 'ilike', searchVal]);
});
break;
case 'selection':
searchValues.forEach(function(searchVal) {
conditions.push([fieldPath, 'ilike', searchVal]);
});
break;
case 'integer': case 'integer':
case 'float': case 'float':
case 'monetary': case 'monetary':
var numValue = parseFloat(value); var numValue = parseFloat(value);
if (!isNaN(numValue)) { if (!isNaN(numValue)) {
conditions.push([fieldName, '=', numValue]); conditions.push([fieldPath, '=', numValue]);
} }
break; break;
case 'boolean': case 'boolean':
var lowerValue = value.toLowerCase().trim(); var lowerValue = value.toLowerCase().trim();
var boolMap = { var boolMap = {
'true': true, 'yes': true, '1': true, 'true': true, 'yes': true, '1': true, 'نعم': true,
'false': false, 'no': false, '0': false 'false': false, 'no': false, '0': false, 'لا': false
}; };
if (lowerValue in boolMap) { if (lowerValue in boolMap) {
conditions.push([fieldName, '=', boolMap[lowerValue]]); conditions.push([fieldPath, '=', boolMap[lowerValue]]);
} }
break; break;
case 'date': case 'date':
case 'datetime': case 'datetime':
if (value.match(/^\d{4}-\d{2}-\d{2}/)) { if (value.match(/^\d{4}-\d{2}-\d{2}/)) {
conditions.push([fieldName, 'ilike', value]); conditions.push([fieldPath, 'ilike', value]);
} }
break; break;
} }
}); });
if (conditions.length === 0) return []; // **الإصلاح الأساسي: التأكد من return domain صحيح دائماً**
if (conditions.length === 1) return conditions[0]; if (conditions.length === 0) {
return [];
}
// دائماً نُرجع array حتى لو condition واحد
if (conditions.length === 1) {
return conditions; // ✅ return [condition] وليس condition
}
// بناء OR domain للـ conditions المتعددة
var orDomain = []; var orDomain = [];
for (var i = 1; i < conditions.length; i++) { for (var i = 1; i < conditions.length; i++) {
orDomain.push('|'); orDomain.push('|');
@ -241,7 +273,12 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
return orDomain.concat(conditions); return orDomain.concat(conditions);
}, },
/**
* جلب الحقول القابلة للبحث - مع دعم computed/related fields
* @returns {Array} قائمة الحقول
*/
_getCustomSearchableFields: function() { _getCustomSearchableFields: function() {
var self = this;
var fields = []; var fields = [];
var state = this.model.get(this.handle); var state = this.model.get(this.handle);
@ -251,53 +288,128 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
var fieldsInfo = state.fieldsInfo; var fieldsInfo = state.fieldsInfo;
var viewType = state.viewType || 'list'; var viewType = state.viewType || 'list';
// محاولة 1: جلب من fieldsInfo (الحقول الموجودة في الـ view)
if (fieldsInfo && fieldsInfo[viewType]) { if (fieldsInfo && fieldsInfo[viewType]) {
Object.keys(fieldsInfo[viewType]).forEach(function(fieldName) { Object.keys(fieldsInfo[viewType]).forEach(function(fieldName) {
var fieldInfo = fieldsInfo[viewType][fieldName]; var fieldInfo = fieldsInfo[viewType][fieldName];
if (fieldInfo && !fieldInfo.invisible && fieldDefs[fieldName]) { if (fieldInfo && !fieldInfo.invisible && fieldDefs[fieldName]) {
var fieldDef = fieldDefs[fieldName]; var fieldDef = fieldDefs[fieldName];
if (fieldDef.store !== false && fieldDef.searchable !== false) {
// **التعديل الأساسي: إزالة شرط store**
// نقبل جميع الحقول القابلة للبحث سواء stored أو computed
if (self._isFieldSearchable(fieldDef)) {
fields.push({ fields.push({
name: fieldName, name: fieldName,
type: fieldDef.type, type: fieldDef.type,
string: fieldDef.string || fieldName string: fieldDef.string || fieldName,
searchPath: self._getFieldSearchPath(fieldName, fieldDef)
}); });
} }
} }
}); });
} }
// محاولة 2: جلب من columns (إذا لم نجد في fieldsInfo)
if (fields.length === 0 && this.renderer && this.renderer.columns) { if (fields.length === 0 && this.renderer && this.renderer.columns) {
this.renderer.columns.forEach(function(col) { this.renderer.columns.forEach(function(col) {
if (!col.invisible && col.attrs && col.attrs.name) { if (!col.invisible && col.attrs && col.attrs.name) {
var fieldName = col.attrs.name; var fieldName = col.attrs.name;
var field = fieldDefs[fieldName]; var field = fieldDefs[fieldName];
if (field && field.store !== false) { if (field && self._isFieldSearchable(field)) {
fields.push({ fields.push({
name: fieldName, name: fieldName,
type: field.type, type: field.type,
string: field.string || fieldName string: field.string || fieldName,
searchPath: self._getFieldSearchPath(fieldName, field)
}); });
} }
} }
}); });
} }
// محاولة 3: حقول افتراضية (fallback)
if (fields.length === 0) { if (fields.length === 0) {
['name', 'display_name', 'code', 'reference'].forEach(function(fname) { ['name', 'display_name', 'login', 'code', 'reference', 'email'].forEach(function(fname) {
if (fieldDefs[fname] && fieldDefs[fname].store !== false) { if (fieldDefs[fname] && self._isFieldSearchable(fieldDefs[fname])) {
fields.push({ fields.push({
name: fname, name: fname,
type: fieldDefs[fname].type, type: fieldDefs[fname].type,
string: fieldDefs[fname].string || fname string: fieldDefs[fname].string || fname,
searchPath: self._getFieldSearchPath(fname, fieldDefs[fname])
}); });
} }
}); });
} }
console.log('FIMS Search: Found', fields.length, 'searchable fields:',
fields.map(f => f.searchPath || f.name).join(', '));
return fields; return fields;
}, },
/**
* التحقق من إمكانية البحث في الحقل
* @param {Object} fieldDef - تعريف الحقل
* @returns {Boolean}
*/
_isFieldSearchable: function(fieldDef) {
if (!fieldDef) return false;
// استبعاد الحقول الخاصة
var excludedTypes = ['binary', 'one2many', 'many2many', 'reference'];
if (excludedTypes.indexOf(fieldDef.type) !== -1) {
return false;
}
// إذا الحقل صرّح أنه غير قابل للبحث
if (fieldDef.searchable === false) {
return false;
}
// **التعديل الأساسي: قبول computed fields**
// stored fields = بحث مباشر
// computed fields = بحث عبر related path إذا وُجد
// related fields = بحث عبر related path
// نقبل الحقل إذا:
// 1. stored field
// 2. computed field مع related
// 3. related field
if (fieldDef.store !== false ||
fieldDef.related ||
fieldDef.compute) {
return true;
}
return false;
},
/**
* الحصول على المسار الصحيح للبحث في الحقل
* @param {String} fieldName - اسم الحقل
* @param {Object} fieldDef - تعريف الحقل
* @returns {String} - المسار للبحث
*/
_getFieldSearchPath: function(fieldName, fieldDef) {
// إذا كان related field، نستخدم الـ related path
if (fieldDef.related && fieldDef.related.length > 0) {
return fieldDef.related.join('.');
}
// إذا كان computed مع related في الـ definition
if (fieldDef.store === false && fieldDef.depends) {
// محاولة استخراج أول depend كـ search path
var depends = fieldDef.depends;
if (depends && depends.length > 0) {
// استخدام أول dependency
return depends[0];
}
}
// default: استخدام اسم الحقل مباشرة
return fieldName;
},
_combineCustomDomains: function(searchDomain) { _combineCustomDomains: function(searchDomain) {
var originalDomain = this._customSearchState.originalDomain || []; var originalDomain = this._customSearchState.originalDomain || [];
@ -323,7 +435,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
_showSearchError: function() { _showSearchError: function() {
if (this.renderer && this.renderer.$) { if (this.renderer && this.renderer.$) {
this.renderer.$('.oe_search_count') this.renderer.$('.oe_search_count')
.text(_t('Search error occurred')) .text(_t('حدث خطأ في البحث'))
.addClass('text-danger') .addClass('text-danger')
.show(); .show();
} }
@ -347,21 +459,17 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
}); });
ListRenderer.include({ ListRenderer.include({
// **إزالة events من class definition لتجنب التعارضات**
// سيتم ربط events programmatically عند الحاجة فقط
init: function() { init: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this._searchTimer = null; this._searchTimer = null;
this._customSearchReady = false; this._customSearchReady = false;
this._lastInputValue = ''; this._lastInputValue = '';
this._customEventsbound = false; // تجنب double binding this._customEventsbound = false;
}, },
_renderView: function () { _renderView: function () {
var self = this; var self = this;
return this._super.apply(this, arguments).then(function (result) { return this._super.apply(this, arguments).then(function (result) {
// تأخير قليل للتأكد من DOM readiness
setTimeout(function() { setTimeout(function() {
try { try {
if (self._shouldAddSearchBox()) { if (self._shouldAddSearchBox()) {
@ -373,7 +481,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
} catch (error) { } catch (error) {
console.error('FIMS Search: Error in _renderView:', error); console.error('FIMS Search: Error in _renderView:', error);
} }
}, 50); // تأخير أكبر قليلاً للاستقرار }, 50);
return result; return result;
}); });
}, },
@ -411,7 +519,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
_shouldAddSearchBox: function() { _shouldAddSearchBox: function() {
try { try {
// **فحوصات الأمان الأساسية**
if (!this.arch || if (!this.arch ||
this.arch.tag !== 'tree' || this.arch.tag !== 'tree' ||
!this.$el || !this.$el ||
@ -421,30 +528,18 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
return false; return false;
} }
// **الحل النهائي المؤكد: فحص hasSelectors**
// embedded tree views: hasSelectors = false أو undefined
// standalone tree views: hasSelectors = true
if (this.hasSelectors === false) { if (this.hasSelectors === false) {
console.log('FIMS Search: Hidden - embedded tree view (hasSelectors = false)');
return false; return false;
} }
// **فحوصات إضافية للأمان:**
// فحص DOM structure كـ backup
if (this.$el.closest('.o_form_view').length > 0) { if (this.$el.closest('.o_form_view').length > 0) {
console.log('FIMS Search: Hidden - inside form view');
return false; return false;
} }
// فحص field widget
if (this.$el.closest('.o_field_widget').length > 0) { if (this.$el.closest('.o_field_widget').length > 0) {
console.log('FIMS Search: Hidden - inside field widget');
return false; return false;
} }
console.log('FIMS Search: Shown - standalone tree view (hasSelectors:', this.hasSelectors, ')');
return true; return true;
} catch (error) { } catch (error) {
@ -455,9 +550,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
_addCustomSearchBox: function() { _addCustomSearchBox: function() {
try { try {
// **التحقق من DOM validity**
if (!this.$el || !this.$el.length) { if (!this.$el || !this.$el.length) {
console.warn('FIMS Search: Invalid DOM element');
return; return;
} }
@ -512,16 +605,14 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
} }
}, },
// **ربط Events programmatically**
_bindCustomEvents: function() { _bindCustomEvents: function() {
if (this._customEventsbound) { if (this._customEventsbound) {
return; // تجنب double binding return;
} }
try { try {
var self = this; var self = this;
// bind events to the search container
this.$el.on('keyup', '.oe_search_input', function(e) { this.$el.on('keyup', '.oe_search_input', function(e) {
self._onCustomSearchKeyUp(e); self._onCustomSearchKeyUp(e);
}); });
@ -544,7 +635,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
} }
}, },
// **تنظيف Events عند destroy**
destroy: function() { destroy: function() {
if (this._customEventsbound) { if (this._customEventsbound) {
this.$el.off('keyup', '.oe_search_input'); this.$el.off('keyup', '.oe_search_input');