Merge pull request #4414 from expsa/eltayar

 [FINAL] odex25_base - موديول البحث العام المحسّن v2.1 - حل نهائي لجميع المشاكل
This commit is contained in:
Mohamed Eltayar 2025-08-29 16:28:41 +03:00 committed by GitHub
commit c20bda9d25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 156 additions and 77 deletions

View File

@ -23,6 +23,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
'<input type="text" class="oe_search_input" placeholder="' + _t('Search in all records...') + '" style="flex: 1; border: 1px solid #ccc; height: 32px; padding: 0 12px; border-radius: 4px;">' +
'<button class="btn btn-sm btn-secondary oe_clear_search ml-2" style="display: none;">' + _t('Clear') + '</button>' +
'<span class="oe_search_count ml-2" style="display: none; color: #6c757d; font-size: 0.9em;"></span>' +
'<span class="oe_search_loading ml-2" style="display: none;"><i class="fa fa-spinner fa-spin"></i> ' + _t('Searching...') + '</span>' +
'</div>';
self.$el.find('table').addClass('oe_table_search');
self.$el.prepend($(search));
@ -39,8 +40,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
this._searchTimeout = null;
this._currentSearchValue = '';
this._isSearching = false;
// Store the original records
this._originalRecords = null;
this._searchMode = false;
},
@ -86,6 +85,9 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
return;
}
// Show loading indicator
this.$el.find('.oe_search_loading').show();
this.$el.find('.oe_search_count').hide();
this._isSearching = true;
// Store original records if not already stored
@ -99,6 +101,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
if (fields.length === 0) {
this._isSearching = false;
this.$el.find('.oe_search_loading').hide();
console.warn('No searchable fields found for model:', model);
return;
}
@ -106,34 +110,82 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
var domain = this._buildSearchDomain(value, fields);
// Add existing domain if any
if (this.state.domain && this.state.domain.length > 0) {
domain = this.state.domain.concat([['id', 'in', this.state.res_ids || []]]).concat(domain);
var baseDomain = this.state.domain || [];
if (baseDomain.length > 0) {
// Combine base domain with search domain
domain = baseDomain.concat(domain);
}
// Perform RPC search
// Get only visible and stored fields to avoid computed fields issues
var fieldsToRead = this._getFieldsToRead();
// Perform RPC search with specific fields only - NO LIMIT
this._rpc({
model: model,
method: 'search_read',
args: [domain],
kwargs: {
fields: this.state.fields_get ? Object.keys(this.state.fields_get) : [],
limit: this.state.limit || 80,
fields: fieldsToRead, // Only read necessary fields
limit: false, // No limit - get all records
offset: 0,
context: this.state.context,
context: this.state.context || session.user_context,
order: this.state.orderedBy ? this.state.orderedBy[0].name : false
}
}).then(function(result) {
self._updateListWithSearchResults(result, value);
self._isSearching = false;
// Restore search value (in case it was cleared during reload)
self.$el.find('.oe_search_input').val(self._currentSearchValue);
}).catch(function(error) {
console.error('Search error:', error);
self._showError(_t('Search failed. Please try again.'));
}).finally(function() {
self._isSearching = false;
self.$el.find('.oe_search_loading').hide();
// Restore search value
self.$el.find('.oe_search_input').val(self._currentSearchValue);
});
},
_getFieldsToRead: function() {
var self = this;
var fields = ['id']; // Always include ID
// Get only visible, stored fields from columns
_.each(this.columns, function(column) {
if (!column.invisible && column.attrs.name) {
var fieldName = column.attrs.name;
var field = self.state.fields[fieldName];
// Only include stored fields or safe field types
if (field) {
// Skip computed fields unless they're stored
if (field.compute && !field.store) {
return; // Skip this field
}
// For many2one, we only need the ID and display_name
if (field.type === 'many2one') {
fields.push(fieldName);
}
// For other fields, include if they're stored
else if (field.store !== false) {
fields.push(fieldName);
}
}
}
});
// Always try to include display_name if available
if (self.state.fields.display_name && !fields.includes('display_name')) {
fields.push('display_name');
}
// Always try to include name if available
if (self.state.fields.name && !fields.includes('name')) {
fields.push('name');
}
return _.uniq(fields);
},
_buildSearchDomain: function(value, fields) {
var domain = [];
var orConditions = [];
@ -150,21 +202,21 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
orConditions.push([field.name, 'ilike', normalizedValue]);
}
}
// For many2one fields
// For many2one fields - search in the name
else if (field.type === 'many2one') {
// Search in the display_name of the related field
// Use the field name directly with ilike
orConditions.push([field.name, 'ilike', value]);
}
// For number fields (if value is numeric)
else if (['integer', 'float', 'monetary'].includes(field.type)) {
if (!isNaN(value)) {
orConditions.push([field.name, '=', parseFloat(value)]);
}
}
// For selection fields
else if (field.type === 'selection') {
orConditions.push([field.name, 'ilike', value]);
}
// For number fields (if value is numeric)
else if (['integer', 'float', 'monetary'].includes(field.type)) {
if (!isNaN(value) && value !== '') {
orConditions.push([field.name, '=', parseFloat(value)]);
}
}
});
// Create OR domain
@ -182,34 +234,49 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
_getSearchableFields: function() {
var self = this;
var fields = [];
var addedFields = {}; // Track added fields to avoid duplicates
// Get visible fields from the list view columns
// Priority 1: Get visible stored fields from columns
_.each(this.columns, function(column) {
if (!column.invisible && column.attrs.name) {
var fieldName = column.attrs.name;
var field = self.state.fields[fieldName];
if (field && field.searchable !== false && field.store !== false) {
fields.push({
name: fieldName,
type: field.type,
string: field.string
});
}
}
});
// If no visible fields, get common searchable fields
if (fields.length === 0) {
var commonFields = ['name', 'display_name', 'reference', 'code', 'description'];
_.each(commonFields, function(fieldName) {
if (self.state.fields[fieldName]) {
var field = self.state.fields[fieldName];
if (field.searchable !== false && field.store !== false) {
if (field && !addedFields[fieldName]) {
// Only searchable in stored fields or specific field types
var isSearchable = (
field.searchable !== false &&
(field.store !== false || field.type === 'many2one') &&
(!field.compute || field.store) // Skip non-stored computed fields
);
if (isSearchable) {
fields.push({
name: fieldName,
type: field.type,
string: field.string
});
addedFields[fieldName] = true;
}
}
}
});
// Priority 2: Add common searchable fields if not enough fields
if (fields.length < 3) {
var commonFields = ['name', 'display_name', 'reference', 'code', 'ref', 'description'];
_.each(commonFields, function(fieldName) {
if (self.state.fields[fieldName] && !addedFields[fieldName]) {
var field = self.state.fields[fieldName];
if (field.searchable !== false &&
field.store !== false &&
(!field.compute || field.store)) {
fields.push({
name: fieldName,
type: field.type,
string: field.string
});
addedFields[fieldName] = true;
}
}
});
@ -224,50 +291,61 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
// Update the count display
var count = records.length;
var $countEl = this.$el.find('.oe_search_count');
if (searchValue && count >= 0) {
$countEl.text(_t('Found: ') + count + _t(' records')).show();
} else {
$countEl.hide();
}
// Update the table rows - filter displayed records
if (records.length > 0) {
var recordIds = records.map(function(r) { return r.id; });
if (count > 0) {
var message = _t('Found: ') + count + _t(' records');
// Show warning if too many records
if (count > 1000) {
message += ' ' + _t('(Large result set, may affect performance)');
}
$countEl.text(message).show();
// Hide rows that don't match
this.$el.find('.o_data_row').each(function() {
var $row = $(this);
var recordId = $row.data('id');
if (recordId) {
var isVisible = recordIds.includes(recordId);
$row.toggle(isVisible);
}
// Update the view with new records
this.state.data.records = records;
this._searchMode = true;
// Re-render the body with new records
this._renderBody().then(function() {
// Restore search input value
self.$el.find('.oe_search_input').val(self._currentSearchValue);
self.$el.find('.oe_clear_search').show();
$countEl.show();
});
// If we need to load more records, update the state
if (count > this.state.data.records.length) {
// Need to reload with new records
this.state.data.records = records;
this._renderBody().then(function() {
// Restore search input value
self.$el.find('.oe_search_input').val(self._currentSearchValue);
self.$el.find('.oe_clear_search').show();
self.$el.find('.oe_search_count').text(_t('Found: ') + count + _t(' records')).show();
});
}
} else {
// No results - hide all rows
this.$el.find('.o_data_row').hide();
// Show no results message
if (!this.$el.find('.oe_no_results').length) {
var noResultsHtml = '<tr class="oe_no_results"><td colspan="100%" class="text-center text-muted">' +
_t('No records found for: ') + _.escape(searchValue) + '</td></tr>';
this.$el.find('tbody').append(noResultsHtml);
}
$countEl.text(_t('No records found')).show();
this._showNoResults(searchValue);
}
},
_showNoResults: function(searchValue) {
// Hide all data rows
this.$el.find('.o_data_row').hide();
// Remove existing no results message
this.$el.find('.oe_no_results').remove();
// Add no results message
var colspan = this.$el.find('thead tr:first th').length || 1;
var noResultsHtml = '<tr class="oe_no_results">' +
'<td colspan="' + colspan + '" class="text-center text-muted" style="padding: 40px;">' +
'<i class="fa fa-search" style="font-size: 3em; margin-bottom: 10px; display: block; opacity: 0.3;"></i>' +
'<h4>' + _t('No records found') + '</h4>' +
'<p>' + _t('No results for: ') + '<strong>' + _.escape(searchValue) + '</strong></p>' +
'<p class="text-muted">' + _t('Try different keywords or check your filters') + '</p>' +
'</td></tr>';
this.$el.find('tbody').append(noResultsHtml);
},
_showError: function(message) {
var $countEl = this.$el.find('.oe_search_count');
$countEl.text(message).css('color', '#dc3545').show();
setTimeout(function() {
$countEl.css('color', '#6c757d');
}, 3000);
},
_clearSearch: function() {
var self = this;
@ -275,13 +353,11 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
this.$el.find('.oe_search_input').val('');
this.$el.find('.oe_clear_search').hide();
this.$el.find('.oe_search_count').hide();
this.$el.find('.oe_search_loading').hide();
// Remove no results message
this.$el.find('.oe_no_results').remove();
// Show all rows again
this.$el.find('.o_data_row').show();
// Reset search state
this._currentSearchValue = '';
this._searchMode = false;
@ -292,6 +368,9 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
this._renderBody().then(function() {
self._originalRecords = null;
});
} else {
// Just show all rows
this.$el.find('.o_data_row').show();
}
},