Fix view update: proper render methods with multiple fallbacks, DOM filtering, and parent reload

This commit is contained in:
Mohamed Eltayar 2025-08-29 17:21:29 +03:00
parent 2bd0e2c9e2
commit 96cf9a42ac
1 changed files with 249 additions and 61 deletions

View File

@ -6,7 +6,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
var _t = core._t;
var session = require('web.session');
var pyUtils = require('web.py_utils');
var Domain = require('web.Domain');
ListRenderer.include({
events: _.extend({}, ListRenderer.prototype.events, {
@ -146,6 +145,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
// Store original data if first search
if (!this._searchState.originalData) {
this._searchState.originalData = this._captureCurrentData();
console.log('Captured original data:', this._searchState.originalData);
}
// Build and execute search
@ -195,15 +195,34 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
* Capture current data state
*/
_captureCurrentData: function() {
// Try to get current records from various locations
if (this.state && this.state.data) {
return {
records: this.state.data.records ? this.state.data.records.slice() : [],
count: this.state.count || 0,
domain: this.state.domain ? this.state.domain.slice() : []
};
var data = {
records: [],
count: 0,
domain: [],
limit: 80,
offset: 0
};
// Get current records
if (this.state) {
if (this.state.data && this.state.data.records) {
data.records = this.state.data.records.slice();
}
if (this.state.count !== undefined) {
data.count = this.state.count;
}
if (this.state.domain) {
data.domain = Array.isArray(this.state.domain) ? this.state.domain.slice() : this.state.domain;
}
if (this.state.limit !== undefined) {
data.limit = this.state.limit;
}
if (this.state.offset !== undefined) {
data.offset = this.state.offset;
}
}
return null;
return data;
},
/**
@ -277,7 +296,21 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
}
// Combine with AND operator
return ['&'].concat(baseDomain).concat(searchDomain);
// Need to be careful with domain structure
var result = [];
// Add AND operator if both domains exist
if (baseDomain.length > 0 && searchDomain.length > 0) {
result.push('&');
}
// Add base domain
result = result.concat(baseDomain);
// Add search domain
result = result.concat(searchDomain);
return result;
},
/**
@ -353,21 +386,21 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
var conditions = [];
var self = this;
// Escape special characters for safe search
var safeValue = this._escapeSearchValue(value);
var normalizedValue = this._normalizeArabic(safeValue);
// Don't escape for now - let's keep it simple
var searchValue = value;
var normalizedValue = this._normalizeArabic(searchValue);
fields.forEach(function(field) {
// Text-based fields
if (['char', 'text', 'html'].includes(field.type)) {
conditions.push([field.name, 'ilike', safeValue]);
if (normalizedValue && normalizedValue !== safeValue) {
conditions.push([field.name, 'ilike', searchValue]);
if (normalizedValue && normalizedValue !== searchValue) {
conditions.push([field.name, 'ilike', normalizedValue]);
}
}
// Selection fields
else if (field.type === 'selection') {
conditions.push([field.name, 'ilike', safeValue]);
conditions.push([field.name, 'ilike', searchValue]);
}
// Number fields
else if (['integer', 'float', 'monetary'].includes(field.type)) {
@ -401,14 +434,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
return domain.concat(conditions);
},
/**
* Escape special characters in search value
*/
_escapeSearchValue: function(value) {
// Remove dangerous characters that might break domain
return value.replace(/[%_\\]/g, '\\$&');
},
/**
* Execute RPC search
*/
@ -419,6 +444,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
console.log('Executing RPC search...');
console.log('Model:', model);
console.log('Domain for RPC:', JSON.stringify(domain));
console.log('Fields to read:', fields);
this._rpc({
@ -433,6 +459,9 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
}
}).then(function(result) {
console.log('Search completed. Found:', result.length, 'records');
if (result.length > 0) {
console.log('First result:', result[0]);
}
self._handleSearchResults(result, searchValue);
}).catch(function(error) {
console.error('RPC Error:', error);
@ -444,14 +473,15 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
},
/**
* Get fields to read
* Get fields to read - CRITICAL: Must match the original fields structure
*/
_getFieldsToRead: function() {
var fields = ['id'];
var fieldsInfo = this._getFieldsInfo();
if (!fieldsInfo) {
return ['id', 'display_name'];
// Return all fields if we can't determine
return false; // This tells Odoo to return all fields
}
// Add visible column fields
@ -462,7 +492,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
var fieldInfo = fieldsInfo[fieldName];
// Skip computed non-stored fields
if (!fieldInfo || (fieldInfo.compute && !fieldInfo.store)) {
if (fieldInfo && fieldInfo.compute && !fieldInfo.store) {
console.log('Skipping computed field:', fieldName);
return;
}
@ -476,6 +507,11 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
fields.push('display_name');
}
// Add __last_update for proper record tracking
if (fieldsInfo.__last_update) {
fields.push('__last_update');
}
return _.uniq(fields);
},
@ -484,25 +520,156 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
*/
_getSearchContext: function() {
var context = _.extend({}, this.state.context || {}, session.user_context || {});
// Remove active_test to include inactive records
delete context.active_test;
// Don't remove active_test - keep original context
return context;
},
/**
* Handle search results
* Handle search results - THIS IS THE CRITICAL PART
*/
_handleSearchResults: function(records, searchValue) {
var count = records.length;
console.log('Handling search results:', count, 'records');
if (count > 0) {
this._updateView(records);
// Update the view with new records
this._updateViewWithRecords(records);
this._showResultCount(count);
} else {
this._showNoResults(searchValue);
}
},
/**
* Update view with search results - PROPER WAY
*/
_updateViewWithRecords: function(records) {
var self = this;
console.log('Updating view with', records.length, 'records');
// Method 1: Update state and trigger re-render
if (this.state && this.state.data) {
// Update the data
this.state.data.records = records;
this.state.data.count = records.length;
this.state.count = records.length;
// Update res_ids if it exists
if (this.state.res_ids) {
this.state.res_ids = records.map(function(r) { return r.id; });
}
console.log('State updated, attempting to re-render...');
// Try different render methods
// Method A: Direct _renderBody
if (typeof this._renderBody === 'function') {
try {
console.log('Calling _renderBody...');
var renderPromise = this._renderBody();
if (renderPromise && typeof renderPromise.then === 'function') {
renderPromise.then(function() {
console.log('_renderBody completed successfully');
self._restoreSearchValue();
}).catch(function(error) {
console.error('_renderBody promise error:', error);
self._tryAlternativeRender(records);
});
} else {
console.log('_renderBody completed (non-promise)');
self._restoreSearchValue();
}
} catch (error) {
console.error('_renderBody error:', error);
this._tryAlternativeRender(records);
}
} else {
console.log('_renderBody not available, trying alternative...');
this._tryAlternativeRender(records);
}
} else {
console.error('No state.data to update');
this._tryAlternativeRender(records);
}
},
/**
* Try alternative rendering method
*/
_tryAlternativeRender: function(records) {
var self = this;
console.log('Trying alternative render method...');
// Method B: Try to trigger parent update
if (this.trigger_up) {
console.log('Triggering update event...');
this.trigger_up('update', {
records: records,
domain: this._searchState.lastSearchDomain
});
}
// Method C: Manual DOM update as last resort
// Hide non-matching rows and show matching ones
var recordIds = records.map(function(r) { return r.id; });
console.log('Record IDs to show:', recordIds);
// First hide all rows
this.$('.o_data_row').hide();
// Then show matching rows
recordIds.forEach(function(id) {
self.$('.o_data_row[data-id="' + id + '"]').show();
});
// If no visible rows after filtering, we might need to render them
var visibleRows = this.$('.o_data_row:visible').length;
console.log('Visible rows after filtering:', visibleRows);
if (visibleRows === 0 && records.length > 0) {
console.log('No visible rows but have records, may need full re-render');
// The records might not be in the current DOM, need full render
this._forceFullRender(records);
}
this._restoreSearchValue();
},
/**
* Force full render of records
*/
_forceFullRender: function(records) {
console.log('Forcing full render...');
// Store records in state
if (this.state && this.state.data) {
this.state.data.records = records;
}
// Try to call parent's reload if available
if (this.getParent && typeof this.getParent === 'function') {
var parent = this.getParent();
if (parent && parent.reload) {
console.log('Calling parent reload...');
parent.reload({
offset: 0,
limit: records.length,
domain: this._searchState.lastSearchDomain
});
}
}
},
/**
* Restore search value in input
*/
_restoreSearchValue: function() {
if (this._searchState && this._searchState.currentValue) {
this.$('.oe_search_input').val(this._searchState.currentValue);
this.$('.oe_clear_search').show();
}
},
/**
* Handle search error
*/
@ -512,27 +679,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
this._fallbackToClientSearch(searchValue);
},
/**
* Update view with search results
*/
_updateView: function(records) {
// This is the tricky part - we need to update the view
// Different versions of Odoo handle this differently
if (this.state && this.state.data) {
this.state.data.records = records;
}
// Try to re-render
if (typeof this._renderBody === 'function') {
try {
this._renderBody();
} catch (e) {
console.error('Error rendering body:', e);
}
}
},
/**
* Fallback to client-side search
*/
@ -542,18 +688,30 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
var searchLower = value.toLowerCase();
var normalizedSearch = this._normalizeArabic(searchLower);
var visibleCount = 0;
var totalCount = 0;
this.$('.o_data_row').each(function() {
var $row = $(this);
totalCount++;
var text = $row.text().toLowerCase();
var normalizedText = this._normalizeArabic(text);
var match = text.includes(searchLower) || normalizedText.includes(normalizedSearch);
var match = text.includes(searchLower) ||
(normalizedText && normalizedSearch && normalizedText.includes(normalizedSearch));
$row.toggle(match);
if (match) visibleCount++;
}.bind(this));
this._showResultCount(visibleCount, true);
// Remove no results message if exists
this.$('.oe_no_results').remove();
if (visibleCount === 0) {
this._showNoResults(value);
} else {
this._showResultCount(visibleCount, true);
}
},
/**
@ -576,11 +734,14 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
// Restore original data
if (this._searchState.originalData) {
this._restoreOriginalData();
} else {
// Just show all rows
this.$('.o_data_row').show();
}
} else {
// Show all rows
this.$('.o_data_row').show();
}
// Show all rows
this.$('.o_data_row').show();
},
/**
@ -591,19 +752,46 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
return;
}
console.log('Restoring original data...');
var originalData = this._searchState.originalData;
if (this.state && this.state.data) {
this.state.data.records = originalData.records;
// Restore state
if (this.state) {
if (this.state.data && originalData.records) {
this.state.data.records = originalData.records;
this.state.data.count = originalData.count || originalData.records.length;
}
if (originalData.count !== undefined) {
this.state.count = originalData.count;
}
if (originalData.res_ids) {
this.state.res_ids = originalData.res_ids;
}
}
// Re-render if possible
// Re-render
if (typeof this._renderBody === 'function') {
try {
this._renderBody();
var renderPromise = this._renderBody();
if (renderPromise && typeof renderPromise.then === 'function') {
renderPromise.then(function() {
console.log('Original data restored successfully');
}).catch(function(error) {
console.error('Error restoring original data:', error);
// Fallback: show all rows
this.$('.o_data_row').show();
}.bind(this));
} else {
console.log('Original data restored');
}
} catch (e) {
console.error('Error restoring view:', e);
// Fallback: show all rows
this.$('.o_data_row').show();
}
} else {
// Fallback: show all rows
this.$('.o_data_row').show();
}
this._searchState.originalData = null;