commit
ab0106ce19
|
|
@ -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');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue