Merge pull request #4414 from expsa/eltayar
✅ [FINAL] odex25_base - موديول البحث العام المحسّن v2.1 - حل نهائي لجميع المشاكل
This commit is contained in:
commit
c20bda9d25
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue