commit
06f5a54bb4
|
|
@ -1,95 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
#
|
||||
# Fortutech IMS Pvt. Ltd.
|
||||
# Copyright (C) 2016-TODAY Fortutech IMS Pvt. Ltd.(<http://www.fortutechims.com>).
|
||||
#
|
||||
###############################################################################
|
||||
{
|
||||
'name': 'General Search in Tree/List View - All Records [ENHANCED]',
|
||||
'name': 'Advanced Tree View Search',
|
||||
'category': 'Tools',
|
||||
'summary': 'Enhanced search across all records in tree/list views with accurate count and optimized performance',
|
||||
'version': '14.0.4.0',
|
||||
'summary': 'Instant search across all visible columns in list views',
|
||||
'version': '14.0.5.0',
|
||||
'license': 'OPL-1',
|
||||
'description': """
|
||||
Enhanced General Search in Tree/List View - FULLY OPTIMIZED & FIXED
|
||||
====================================================================
|
||||
|
||||
This module provides powerful, accurate search functionality for all tree/list views
|
||||
using the most advanced Odoo integration methodology:
|
||||
|
||||
✅ CORE FUNCTIONALITY:
|
||||
* Searches across ALL records in the database (not just visible page records)
|
||||
* Searches in ALL visible columns of the current list view automatically
|
||||
* Uses proper Odoo trigger_up('do_search') methodology - ZERO custom DOM manipulation
|
||||
* Maintains 100% compatibility with all Odoo functionality
|
||||
* Records are fully clickable and work exactly like standard Odoo
|
||||
* Perfect integration with existing search domains, filters, and grouping
|
||||
|
||||
🎯 ENHANCED FEATURES (Version 4.0):
|
||||
* Accurate record count using RPC search_count method
|
||||
* Real-time search with 500ms intelligent debounce
|
||||
* Advanced Arabic text normalization and search support
|
||||
* Multi-field type support: char, text, integer, float, many2one, selection, boolean
|
||||
* Enhanced loading states with clear user feedback
|
||||
* Comprehensive error handling and graceful fallbacks
|
||||
* Smart field detection from visible columns
|
||||
* Advanced domain combination logic
|
||||
* Memory leak prevention and proper cleanup
|
||||
|
||||
🚀 TECHNICAL EXCELLENCE:
|
||||
* Fixed critical duplicate method definitions
|
||||
* Enhanced mutex-based concurrency control
|
||||
* Proper domain deep copying to avoid reference issues
|
||||
* Optimized search state management and restoration
|
||||
* Comprehensive logging for debugging and monitoring
|
||||
* Zero interference with Odoo's native rendering
|
||||
* Clean separation between UI and business logic
|
||||
|
||||
🌍 MULTILINGUAL SUPPORT:
|
||||
* Full Arabic text support with advanced normalization
|
||||
* Handles Arabic diacritics, digit variations, and character normalization
|
||||
* Support for various text encodings and formats
|
||||
* Intelligent boolean value matching (yes/no, نعم/لا, true/false)
|
||||
|
||||
💡 USER EXPERIENCE:
|
||||
* Intuitive search interface with clear visual feedback
|
||||
* Shows accurate count of found records across ALL pages
|
||||
* Clear button for easy search reset
|
||||
* Loading indicators during search operations
|
||||
* Non-intrusive design that complements existing UI
|
||||
* Maintains all standard Odoo keyboard shortcuts and interactions
|
||||
|
||||
🔧 COMPATIBILITY & INTEGRATION:
|
||||
* Works with all Odoo 14 list/tree views automatically
|
||||
* Compatible with existing search domains and filters
|
||||
* Respects view permissions and access rights
|
||||
* Works with grouped views and complex domains
|
||||
* Integrates seamlessly with existing customizations
|
||||
* Zero configuration required - works out of the box
|
||||
|
||||
Version 4.0 - COMPLETE OPTIMIZATION & BUG FIXES:
|
||||
------------------------------------------------
|
||||
✅ FIXED: Duplicate _renderView method definitions causing conflicts
|
||||
✅ FIXED: Inaccurate record counting (now uses proper RPC search_count)
|
||||
✅ FIXED: Search state restoration issues and infinite loops
|
||||
✅ ENHANCED: Domain combination logic with deep copying
|
||||
✅ ENHANCED: Field type detection and search logic
|
||||
✅ ENHANCED: Error handling and graceful degradation
|
||||
✅ ENHANCED: Performance optimizations and memory management
|
||||
✅ ENHANCED: Arabic text normalization algorithms
|
||||
✅ ENHANCED: User interface feedback and loading states
|
||||
|
||||
Implementation Highlights:
|
||||
-------------------------
|
||||
- Uses official Odoo ListRenderer.include() pattern
|
||||
- Implements proper trigger_up('do_search') for filtering
|
||||
- Leverages RPC search_count for accurate record counting
|
||||
- Builds standard Odoo domain syntax for maximum compatibility
|
||||
- Maintains original view domains while adding search conditions
|
||||
- Zero custom record rendering - uses Odoo's native methods
|
||||
- Comprehensive field type support with intelligent matching
|
||||
- Advanced Arabic text processing and normalization
|
||||
Advanced search functionality for Odoo list views.
|
||||
Provides instant search across all visible columns with real-time filtering and accurate record counting.
|
||||
""",
|
||||
'depends': ['base', 'web'],
|
||||
'author': "Fortutech IMS Pvt. Ltd.",
|
||||
|
|
@ -97,8 +15,8 @@
|
|||
'data': [
|
||||
'views/assets.xml',
|
||||
],
|
||||
"installable": True,
|
||||
"application": False,
|
||||
"auto_install": False,
|
||||
"images": ['static/description/banner.png'],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
'images': ['static/description/banner.png'],
|
||||
}
|
||||
|
|
@ -1,123 +1,41 @@
|
|||
<section class="oe_container oe_dark lead">
|
||||
<div class="oe_row" style="width: 100% !important;">
|
||||
<div class="oe_span12" style="width: 100% !important;">
|
||||
<div class="panel panel-primary" style="border:none">
|
||||
<div class="panel-body">
|
||||
<ul class="list-unstyled">
|
||||
<div class="alert alert-info" style="background-color:#ed2f2f;color:#FFF"><i class="fa fa-hand-o-right"></i> <strong> Key Features </strong></div>
|
||||
<li><i class="fa fa-key" style="color:#ed2f2f"></i> This Module is used to each list or tree views.</li>
|
||||
<li><i class="fa fa-key" style="color:#ed2f2f"></i> Immediately responce when you press any Key in quick search field.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Advanced Tree View Search</title>
|
||||
</head>
|
||||
<body>
|
||||
<section class="container">
|
||||
<div style="text-align: center; margin-bottom: 20px;">
|
||||
<img src="banner.png" alt="Advanced Tree View Search" style="max-width: 100%; height: auto;">
|
||||
</div>
|
||||
|
||||
<h1 style="color: #2c3e50; text-align: center;">Advanced Tree View Search</h1>
|
||||
|
||||
<p style="font-size: 16px; text-align: center; margin: 20px 0;">
|
||||
Instant search across all visible columns in Odoo list views with real-time filtering.
|
||||
</p>
|
||||
|
||||
<h2 style="color: #34495e;">Features</h2>
|
||||
<ul style="font-size: 14px; line-height: 1.8;">
|
||||
<li>Search all records in database (not just visible page)</li>
|
||||
<li>Real-time search with intelligent debounce</li>
|
||||
<li>Accurate record counting</li>
|
||||
<li>Works with all field types</li>
|
||||
<li>Automatic RTL/LTR support</li>
|
||||
<li>No configuration required</li>
|
||||
</ul>
|
||||
|
||||
<h2 style="color: #34495e;">Screenshots</h2>
|
||||
<div style="margin: 20px 0;">
|
||||
<img src="1_search.png" alt="Search Box" style="max-width: 100%; margin: 10px 0;">
|
||||
<img src="2_result.png" alt="Search Results" style="max-width: 100%; margin: 10px 0;">
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 30px; padding: 20px; background: #f8f9fa;">
|
||||
<img src="fims_logo.png" alt="Fortutech IMS" style="max-width: 200px;">
|
||||
<p style="margin-top: 10px;">© Fortutech IMS Pvt. Ltd.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="oe_container oe_dark lead">
|
||||
<div class="oe_row" style="width: 100% !important;">
|
||||
<div class="oe_span12" style="width: 100% !important;">
|
||||
<div class="panel panel-primary" style="border:none">
|
||||
<div class="panel-body">
|
||||
<div class="alert alert-info" style="background-color:#ed2f2f;color:#FFF">
|
||||
<i class="fa fa-hand-o-right"></i>
|
||||
<strong>Search</strong>
|
||||
</div>
|
||||
<p>
|
||||
<img class="oe_picture oe_screenshot" src="1_search.png" alt="" style="width: 82% !important;">
|
||||
</p>
|
||||
<p>
|
||||
<img class="oe_picture oe_screenshot" src="3_form_search.png" alt="" style="width: 82% !important;">
|
||||
</p>
|
||||
<div class="alert alert-info" style="background-color:#ed2f2f;color:#FFF">
|
||||
<i class="fa fa-hand-o-right"></i>
|
||||
<strong>Result</strong>
|
||||
</div>
|
||||
<p>
|
||||
|
||||
<img class="oe_picture oe_screenshot" src="2_result.png" alt="" style="width: 82% !important;">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="oe_container oe_dark lead">
|
||||
<div class="oe_row" style="width: 100% !important;">
|
||||
<div class="oe_span12" style="width: 100% !important;">
|
||||
<div class="panel panel-primary" style="border:none">
|
||||
<div class="panel-body" style="text-align: center;">
|
||||
<ul class="list-unstyled">
|
||||
<div class="alert alert-info" style="background-color:#ed2f2f;color:#FFF"><i class="fa fa-globe" aria-hidden="true"></i> <strong> Our Service </strong></div>
|
||||
</ul>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Customization.png" alt="" style="width:60px;height:60px;"></th>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Implementation.png" alt="" style="width:60px;height:60px;"></th>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Integration.png" alt="" style="width:60px;height:60px;"></th>
|
||||
</tr>
|
||||
<tr style="height: 40px;font-size: 18px;">
|
||||
<td style="text-align: center;width: 33%;">Odoo Customization</td>
|
||||
<td style="text-align: center;width: 33%;">Odoo Implementation</td>
|
||||
<td style="text-align: center;width: 33%;">Odoo Integration</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br></br>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Migration.png" alt="" style="width:60px;height:60px;"></th>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Training.png" alt="" style="width:60px;height:60px;"></th>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Installation.png" alt="" style="width:60px;height:60px;"></th>
|
||||
</tr>
|
||||
<tr style="height: 40px;font-size: 18px;">
|
||||
<td style="text-align: center;width: 33%;">Odoo Migration</td>
|
||||
<td style="text-align: center;width: 33%;">Odoo Training</td>
|
||||
<td style="text-align: center;width: 33%;">Odoo Installation</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br></br>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Consulting.png" alt="" style="width:60px;height:60px;"></th>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Support.png" alt="" style="width:60px;height:60px;"></th>
|
||||
<th style="text-align: center;width: 33%;"><img src="service/Hire.png" alt="" style="width:60px;height:60px;"></th>
|
||||
</tr>
|
||||
<tr style="height: 40px;font-size: 18px;">
|
||||
<td style="text-align: center;width: 33%;">Odoo Consulting</td>
|
||||
<td style="text-align: center;width: 33%;">Odoo Support</td>
|
||||
<td style="text-align: center;width: 33%;">Hire Developer</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="oe_container oe_dark lead">
|
||||
<div class="oe_row" style="width: 100% !important;">
|
||||
<div class="oe_span12" style="width: 100% !important;">
|
||||
<div class="panel panel-primary" style="border:none">
|
||||
<div class="panel-body" style="text-align: center;">
|
||||
<ul class="list-unstyled">
|
||||
<div class="alert alert-info" style="background-color:#ed2f2f;color:#FFF"><i class="fa fa-life-ring" aria-hidden="true"></i> <strong> Get Help & Support </strong></div>
|
||||
<li><i class="fa fa-key" style="color:#ed2f2f"></i> You will get 30 Days free support incase any bugs or issue.</li>
|
||||
<br>
|
||||
<li><img style="display: block;margin: 0 auto;height: 211px;" class="oe_picture oe_screenshot" src="fims_logo.png" alt=""></li>
|
||||
<br><br>
|
||||
</ul>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th style="text-align: center;"><img src="website.png" alt="" style="width:70px;height:70px;"></th>
|
||||
<th style="text-align: center;"><img src="mail.png" alt="" style="width:70px;height:70px;"></th>
|
||||
<th style="text-align: center;"><img src="skype.png" alt="" style="width:70px;height:70px;"></th>
|
||||
</tr>
|
||||
<tr style="height: 61px;font-size: 20px;">
|
||||
<td style="text-align: center;">www.fortutechims.com</td>
|
||||
<td style="text-align: center;">info@fortutechims.com</td>
|
||||
<td style="text-align: center;">info@fortutechims.com</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -11,45 +11,41 @@
|
|||
padding: 12px;
|
||||
}
|
||||
|
||||
/* Enhanced search container styles */
|
||||
.oe_search_container {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 12px;
|
||||
margin: 8px;
|
||||
border: 1px solid #dee2e6;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
transition: all 0.3s ease;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.oe_search_container:hover {
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
/* Enhanced search input styles */
|
||||
.oe_search_input {
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #ced4da !important;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.oe_search_input:focus {
|
||||
border-color: #007bff !important;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25) !important;
|
||||
outline: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.oe_search_input::placeholder {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Enhanced clear button */
|
||||
.oe_clear_search {
|
||||
min-width: 70px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.oe_clear_search:hover {
|
||||
|
|
@ -58,80 +54,24 @@
|
|||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Enhanced search count display */
|
||||
.oe_search_count {
|
||||
color: #28a745;
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
padding: 4px 8px;
|
||||
background-color: rgba(40, 167, 69, 0.1);
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(40, 167, 69, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
padding: 6px 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Enhanced loading indicator */
|
||||
.oe_search_loading {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.oe_search_loading i {
|
||||
animation: enhanced-search-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* Responsive design improvements */
|
||||
@media (max-width: 768px) {
|
||||
.oe_search_container {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.oe_search_input {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.oe_clear_search,
|
||||
.oe_search_count {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced loading animation */
|
||||
@keyframes enhanced-search-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Accessibility improvements */
|
||||
.oe_search_input:focus-visible {
|
||||
outline: 2px solid #007bff;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.oe_clear_search:focus-visible {
|
||||
outline: 2px solid #007bff;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Arabic text support enhancements */
|
||||
.oe_search_input[dir="rtl"] {
|
||||
text-align: right;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.oe_search_container[dir="rtl"] {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.oe_search_container[dir="rtl"] .oe_clear_search {
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.oe_search_container[dir="rtl"] .oe_search_count {
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
|
@ -9,17 +9,9 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
var rpc = require('web.rpc');
|
||||
var session = require('web.session');
|
||||
|
||||
// ================================
|
||||
// ListController: Data operations
|
||||
// ================================
|
||||
ListController.include({
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
// Initialize search state
|
||||
this._customSearchState = {
|
||||
timer: null,
|
||||
value: '',
|
||||
|
|
@ -28,54 +20,34 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
originalDomain: null,
|
||||
searchInProgress: false,
|
||||
lastSearchPromise: null,
|
||||
lastSearchValue: '' // Track last search value
|
||||
lastSearchValue: ''
|
||||
};
|
||||
this._searchMutex = new concurrency.Mutex();
|
||||
},
|
||||
|
||||
/**
|
||||
* @override - Hook into rendering complete
|
||||
*/
|
||||
renderButtons: function () {
|
||||
this._super.apply(this, arguments);
|
||||
// Setup search handler after buttons are rendered
|
||||
this._setupCustomSearchHandler();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup custom search handler
|
||||
*/
|
||||
_setupCustomSearchHandler: function() {
|
||||
var self = this;
|
||||
// Ensure renderer is ready
|
||||
if (this.renderer && this.renderer._customSearchReady) {
|
||||
console.log('Custom search handler already setup');
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle custom search from controller
|
||||
*/
|
||||
_handleCustomSearch: function(value) {
|
||||
var self = this;
|
||||
|
||||
// Check if value actually changed
|
||||
if (value === self._customSearchState.lastSearchValue) {
|
||||
console.log('Search value unchanged, skipping');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Update last search value
|
||||
self._customSearchState.lastSearchValue = value;
|
||||
|
||||
// Cancel any pending search
|
||||
if (self._customSearchState.timer) {
|
||||
clearTimeout(self._customSearchState.timer);
|
||||
self._customSearchState.timer = null;
|
||||
}
|
||||
|
||||
// Debounce search
|
||||
return new Promise(function(resolve) {
|
||||
self._customSearchState.timer = setTimeout(function() {
|
||||
self._executeCustomSearch(value).then(resolve);
|
||||
|
|
@ -83,36 +55,26 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute the search
|
||||
*/
|
||||
_executeCustomSearch: function(value) {
|
||||
var self = this;
|
||||
|
||||
if (self._customSearchState.searchInProgress) {
|
||||
console.log('Search already in progress, queueing');
|
||||
return self._customSearchState.lastSearchPromise.then(function() {
|
||||
return self._executeCustomSearch(value);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('=== EXECUTING CUSTOM SEARCH ===');
|
||||
console.log('Search value:', value);
|
||||
|
||||
var searchPromise = this._searchMutex.exec(function() {
|
||||
self._customSearchState.searchInProgress = true;
|
||||
|
||||
if (!value || value.length === 0) {
|
||||
console.log('Empty search - clearing filters');
|
||||
return self._clearCustomSearch();
|
||||
}
|
||||
|
||||
// Store original domain on first search
|
||||
if (!self._customSearchState.originalDomain && !self._customSearchState.isFiltered) {
|
||||
var currentState = self.model.get(self.handle);
|
||||
if (currentState && currentState.domain) {
|
||||
self._customSearchState.originalDomain = JSON.parse(JSON.stringify(currentState.domain));
|
||||
console.log('Stored original domain:', self._customSearchState.originalDomain);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,87 +88,55 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
return searchPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply search using proper Odoo 14 method
|
||||
*/
|
||||
_applyCustomSearch: function(value) {
|
||||
var self = this;
|
||||
|
||||
// Build search domain
|
||||
var searchDomain = this._buildCustomSearchDomain(value);
|
||||
|
||||
if (!searchDomain || searchDomain.length === 0) {
|
||||
console.warn('No searchable fields found or no valid search domain');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Combine with original domain
|
||||
var finalDomain = this._combineCustomDomains(searchDomain);
|
||||
|
||||
console.log('Search domain:', searchDomain);
|
||||
console.log('Final domain:', finalDomain);
|
||||
|
||||
// Show loading state
|
||||
if (self.renderer) {
|
||||
self.renderer.$('.oe_search_loading').show();
|
||||
self.renderer.$('.oe_search_count').hide();
|
||||
}
|
||||
|
||||
// First get the count
|
||||
return rpc.query({
|
||||
model: this.modelName,
|
||||
method: 'search_count',
|
||||
args: [finalDomain],
|
||||
context: session.user_context
|
||||
}).then(function(count) {
|
||||
console.log('Found records:', count);
|
||||
|
||||
// Update UI with count
|
||||
self._customSearchState.filteredCount = count;
|
||||
self._updateCustomSearchUI(count);
|
||||
|
||||
// Now reload the view with new domain
|
||||
// CRITICAL FIX: Use proper reload method for Odoo 14
|
||||
var handle = self.handle;
|
||||
var state = self.model.get(handle);
|
||||
|
||||
// Update the domain in the state
|
||||
return self.model.reload(handle, {
|
||||
domain: finalDomain,
|
||||
offset: 0, // Reset to first page
|
||||
offset: 0,
|
||||
limit: state.limit || 80
|
||||
});
|
||||
}).then(function() {
|
||||
// Update the view
|
||||
self._customSearchState.isFiltered = true;
|
||||
self._customSearchState.value = value;
|
||||
|
||||
// Trigger update to renderer WITHOUT reload
|
||||
// This maintains the search box value
|
||||
return self.update({}, {reload: false});
|
||||
}).then(function() {
|
||||
console.log('Search applied successfully');
|
||||
return Promise.resolve();
|
||||
}).catch(function(error) {
|
||||
console.error('Search error:', error);
|
||||
self._showSearchError();
|
||||
return Promise.resolve();
|
||||
}).finally(function() {
|
||||
// Hide loading
|
||||
if (self.renderer) {
|
||||
self.renderer.$('.oe_search_loading').hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear custom search
|
||||
*/
|
||||
_clearCustomSearch: function() {
|
||||
var self = this;
|
||||
|
||||
console.log('=== CLEARING CUSTOM SEARCH ===');
|
||||
|
||||
// Clear UI immediately
|
||||
if (this.renderer && this.renderer.$) {
|
||||
this.renderer.$('.oe_search_input').val('');
|
||||
this.renderer.$('.oe_clear_search').hide();
|
||||
|
|
@ -214,19 +144,14 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
this.renderer.$('.oe_search_loading').show();
|
||||
}
|
||||
|
||||
// Reset state
|
||||
this._customSearchState.value = '';
|
||||
this._customSearchState.isFiltered = false;
|
||||
this._customSearchState.filteredCount = 0;
|
||||
this._customSearchState.lastSearchValue = '';
|
||||
|
||||
// Get original domain
|
||||
var originalDomain = this._customSearchState.originalDomain || [];
|
||||
this._customSearchState.originalDomain = null;
|
||||
|
||||
console.log('Restoring original domain:', originalDomain);
|
||||
|
||||
// Reload with original domain
|
||||
var handle = this.handle;
|
||||
var state = this.model.get(handle);
|
||||
|
||||
|
|
@ -243,101 +168,63 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Build search domain - FIXED VERSION
|
||||
*/
|
||||
_buildCustomSearchDomain: function(value) {
|
||||
if (!value) return [];
|
||||
|
||||
var fields = this._getCustomSearchableFields();
|
||||
if (!fields || fields.length === 0) {
|
||||
console.warn('No searchable fields found');
|
||||
return [];
|
||||
}
|
||||
if (!fields || fields.length === 0) return [];
|
||||
|
||||
var conditions = [];
|
||||
var normalized = this._normalizeArabic(value);
|
||||
var normalized = this._normalizeText(value);
|
||||
var searchValues = [value];
|
||||
if (normalized !== value) {
|
||||
searchValues.push(normalized);
|
||||
}
|
||||
|
||||
console.log('Building domain for fields:', fields);
|
||||
|
||||
fields.forEach(function(field) {
|
||||
try {
|
||||
var fieldType = field.type;
|
||||
var fieldName = field.name;
|
||||
|
||||
switch(fieldType) {
|
||||
case 'char':
|
||||
case 'text':
|
||||
case 'html':
|
||||
searchValues.forEach(function(searchVal) {
|
||||
conditions.push([fieldName, 'ilike', searchVal]);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'integer':
|
||||
case 'float':
|
||||
case 'monetary':
|
||||
var numValue = parseFloat(value);
|
||||
if (!isNaN(numValue)) {
|
||||
// For numeric fields, also search as string
|
||||
conditions.push([fieldName, '=', numValue]);
|
||||
// Also search string representation
|
||||
conditions.push([fieldName, 'ilike', value]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'many2one':
|
||||
// CRITICAL FIX: For many2one, search on display_name
|
||||
searchValues.forEach(function(searchVal) {
|
||||
// Direct search on the field (searches display_name by default)
|
||||
conditions.push([fieldName, 'ilike', searchVal]);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'selection':
|
||||
searchValues.forEach(function(searchVal) {
|
||||
conditions.push([fieldName, 'ilike', searchVal]);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'boolean':
|
||||
var lowerValue = value.toLowerCase().trim();
|
||||
var booleanMappings = {
|
||||
'true': true, 'yes': true, 'نعم': true, '1': true, 'صح': true,
|
||||
'false': false, 'no': false, 'لا': false, '0': false, 'خطأ': false
|
||||
};
|
||||
if (lowerValue in booleanMappings) {
|
||||
conditions.push([fieldName, '=', booleanMappings[lowerValue]]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
// Try to parse as date
|
||||
if (value.match(/^\d{4}-\d{2}-\d{2}/)) {
|
||||
conditions.push([fieldName, 'ilike', value]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Error processing field', field.name, ':', error);
|
||||
var fieldType = field.type;
|
||||
var fieldName = field.name;
|
||||
|
||||
switch(fieldType) {
|
||||
case 'char':
|
||||
case 'text':
|
||||
case 'html':
|
||||
case 'many2one':
|
||||
case 'selection':
|
||||
searchValues.forEach(function(searchVal) {
|
||||
conditions.push([fieldName, 'ilike', searchVal]);
|
||||
});
|
||||
break;
|
||||
case 'integer':
|
||||
case 'float':
|
||||
case 'monetary':
|
||||
var numValue = parseFloat(value);
|
||||
if (!isNaN(numValue)) {
|
||||
conditions.push([fieldName, '=', numValue]);
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
var lowerValue = value.toLowerCase().trim();
|
||||
var boolMap = {
|
||||
'true': true, 'yes': true, '1': true,
|
||||
'false': false, 'no': false, '0': false
|
||||
};
|
||||
if (lowerValue in boolMap) {
|
||||
conditions.push([fieldName, '=', boolMap[lowerValue]]);
|
||||
}
|
||||
break;
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
if (value.match(/^\d{4}-\d{2}-\d{2}/)) {
|
||||
conditions.push([fieldName, 'ilike', value]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (conditions.length === 0) {
|
||||
return [];
|
||||
}
|
||||
if (conditions.length === 0) return [];
|
||||
if (conditions.length === 1) return conditions[0];
|
||||
|
||||
// Build proper OR domain
|
||||
if (conditions.length === 1) {
|
||||
return conditions[0];
|
||||
}
|
||||
|
||||
// Create OR domain: ['|', ['field1', 'op', 'val'], ['field2', 'op', 'val']]
|
||||
var orDomain = [];
|
||||
for (var i = 1; i < conditions.length; i++) {
|
||||
orDomain.push('|');
|
||||
|
|
@ -345,23 +232,16 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
return orDomain.concat(conditions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get searchable fields - ENHANCED VERSION
|
||||
*/
|
||||
_getCustomSearchableFields: function() {
|
||||
var fields = [];
|
||||
var state = this.model.get(this.handle);
|
||||
|
||||
if (!state) {
|
||||
console.warn('No state available');
|
||||
return fields;
|
||||
}
|
||||
if (!state) return fields;
|
||||
|
||||
var fieldDefs = state.fields || {};
|
||||
var fieldsInfo = state.fieldsInfo;
|
||||
var viewType = state.viewType || 'list';
|
||||
|
||||
// Method 1: Get from fieldsInfo (most reliable)
|
||||
if (fieldsInfo && fieldsInfo[viewType]) {
|
||||
Object.keys(fieldsInfo[viewType]).forEach(function(fieldName) {
|
||||
var fieldInfo = fieldsInfo[viewType][fieldName];
|
||||
|
|
@ -378,7 +258,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
// Method 2: Get from renderer columns if available
|
||||
if (fields.length === 0 && this.renderer && this.renderer.columns) {
|
||||
this.renderer.columns.forEach(function(col) {
|
||||
if (!col.invisible && col.attrs && col.attrs.name) {
|
||||
|
|
@ -395,10 +274,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
// Method 3: Fallback to common searchable fields
|
||||
if (fields.length === 0) {
|
||||
var commonFields = ['name', 'display_name', 'code', 'reference', 'ref', 'description'];
|
||||
commonFields.forEach(function(fname) {
|
||||
['name', 'display_name', 'code', 'reference'].forEach(function(fname) {
|
||||
if (fieldDefs[fname] && fieldDefs[fname].store !== false) {
|
||||
fields.push({
|
||||
name: fname,
|
||||
|
|
@ -409,106 +286,57 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
console.log('Searchable fields found:', fields);
|
||||
return fields;
|
||||
},
|
||||
|
||||
/**
|
||||
* Combine domains - FIXED VERSION
|
||||
*/
|
||||
_combineCustomDomains: function(searchDomain) {
|
||||
var originalDomain = this._customSearchState.originalDomain || [];
|
||||
|
||||
// Handle empty domains
|
||||
if (!searchDomain || searchDomain.length === 0) {
|
||||
return originalDomain;
|
||||
}
|
||||
if (!originalDomain || originalDomain.length === 0) {
|
||||
return searchDomain;
|
||||
}
|
||||
if (!searchDomain || searchDomain.length === 0) return originalDomain;
|
||||
if (!originalDomain || originalDomain.length === 0) return searchDomain;
|
||||
|
||||
// Combine with AND operator
|
||||
// Ensure we're working with arrays
|
||||
var origArray = Array.isArray(originalDomain) ? originalDomain : [originalDomain];
|
||||
var searchArray = Array.isArray(searchDomain) ? searchDomain : [searchDomain];
|
||||
|
||||
// Create combined domain: ['&', original_domain, search_domain]
|
||||
return ['&'].concat(origArray).concat(searchArray);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update search UI
|
||||
*/
|
||||
_updateCustomSearchUI: function(count) {
|
||||
if (this.renderer && this.renderer.$) {
|
||||
var isArabic = this._isArabicLanguage();
|
||||
var message = isArabic ?
|
||||
'عدد السجلات: ' + count :
|
||||
_t('Found: ') + count + _t(' records');
|
||||
|
||||
this.renderer.$('.oe_search_count')
|
||||
.text(message)
|
||||
.text(_t('Found: ') + count + _t(' records'))
|
||||
.removeClass('text-danger')
|
||||
.show();
|
||||
this.renderer.$('.oe_clear_search').show();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show search error
|
||||
*/
|
||||
_showSearchError: function() {
|
||||
if (this.renderer && this.renderer.$) {
|
||||
var isArabic = this._isArabicLanguage();
|
||||
var errorMsg = isArabic ? 'حدث خطأ في البحث' : _t('Search error occurred');
|
||||
|
||||
this.renderer.$('.oe_search_count')
|
||||
.text(errorMsg)
|
||||
.text(_t('Search error occurred'))
|
||||
.addClass('text-danger')
|
||||
.show();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if system language is Arabic
|
||||
*/
|
||||
_isArabicLanguage: function() {
|
||||
var lang = session.user_context.lang || '';
|
||||
return lang.startsWith('ar');
|
||||
},
|
||||
|
||||
/**
|
||||
* Normalize Arabic text - ENHANCED VERSION
|
||||
*/
|
||||
_normalizeArabic: function(text) {
|
||||
_normalizeText: function(text) {
|
||||
if (!text) return '';
|
||||
|
||||
return text
|
||||
// Remove Arabic diacritics
|
||||
.replace(/[\u064B-\u065F]/g, '')
|
||||
// Convert Arabic-Indic digits to Western digits
|
||||
.replace(/[٠-٩]/g, function(d) {
|
||||
return String.fromCharCode(d.charCodeAt(0) - 0x0660 + 0x0030);
|
||||
})
|
||||
// Convert Persian digits to Western digits
|
||||
.replace(/[۰-۹]/g, function(d) {
|
||||
return String.fromCharCode(d.charCodeAt(0) - 0x06F0 + 0x0030);
|
||||
})
|
||||
// Normalize Alef variations
|
||||
.replace(/[آأإا]/g, 'ا')
|
||||
// Normalize Teh Marbuta
|
||||
.replace(/ة/g, 'ه')
|
||||
// Normalize Yeh variations
|
||||
.replace(/[يئى]/g, 'ي')
|
||||
// Normalize Waw variations
|
||||
.replace(/[ؤو]/g, 'و')
|
||||
// Trim spaces
|
||||
.trim();
|
||||
}
|
||||
});
|
||||
|
||||
// ================================
|
||||
// ListRenderer: UI only
|
||||
// ================================
|
||||
ListRenderer.include({
|
||||
events: _.extend({}, ListRenderer.prototype.events, {
|
||||
'keyup .oe_search_input': '_onCustomSearchKeyUp',
|
||||
|
|
@ -517,47 +345,33 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
'keydown .oe_search_input': '_onCustomSearchKeyDown'
|
||||
}),
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this._searchTimer = null;
|
||||
this._customSearchReady = false;
|
||||
this._lastInputValue = ''; // Track last input value
|
||||
this._lastInputValue = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_renderView: function () {
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function (result) {
|
||||
// Add search box for tree views
|
||||
if (self._shouldAddSearchBox()) {
|
||||
self._addCustomSearchBox();
|
||||
self._customSearchReady = true;
|
||||
|
||||
// Restore search state after render
|
||||
self._restoreSearchState();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore search state after render
|
||||
*/
|
||||
_restoreSearchState: function() {
|
||||
var controller = this.getParent();
|
||||
if (controller && controller._customSearchState) {
|
||||
var state = controller._customSearchState;
|
||||
|
||||
// Restore search input value
|
||||
if (state.value) {
|
||||
var $input = this.$('.oe_search_input');
|
||||
$input.val(state.value);
|
||||
// Set cursor position to end of text
|
||||
var length = state.value.length;
|
||||
if ($input[0] && $input[0].setSelectionRange) {
|
||||
$input[0].setSelectionRange(length, length);
|
||||
|
|
@ -566,32 +380,15 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
this._lastInputValue = state.value;
|
||||
}
|
||||
|
||||
// Restore count display
|
||||
if (state.isFiltered && state.filteredCount >= 0) {
|
||||
var isArabic = this._isArabicLanguage();
|
||||
var message = isArabic ?
|
||||
'عدد السجلات: ' + state.filteredCount :
|
||||
_t('Found: ') + state.filteredCount + _t(' records');
|
||||
|
||||
this.$('.oe_search_count')
|
||||
.text(message)
|
||||
.text(_t('Found: ') + state.filteredCount + _t(' records'))
|
||||
.removeClass('text-danger')
|
||||
.show();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if system language is Arabic
|
||||
*/
|
||||
_isArabicLanguage: function() {
|
||||
var lang = session.user_context.lang || '';
|
||||
return lang.startsWith('ar');
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if we should add search box
|
||||
*/
|
||||
_shouldAddSearchBox: function() {
|
||||
return this.arch &&
|
||||
this.arch.tag === 'tree' &&
|
||||
|
|
@ -600,11 +397,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
!this.$el.find('.oe_search_container').length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add search box UI
|
||||
*/
|
||||
_addCustomSearchBox: function() {
|
||||
// Check if already exists or if we have a saved state
|
||||
var controller = this.getParent();
|
||||
var savedValue = '';
|
||||
var savedCount = 0;
|
||||
|
|
@ -616,44 +409,31 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
isFiltered = controller._customSearchState.isFiltered || false;
|
||||
}
|
||||
|
||||
var isArabic = this._isArabicLanguage();
|
||||
var dir = isArabic ? 'rtl' : 'ltr';
|
||||
|
||||
// Translations
|
||||
var placeholder = isArabic ? 'البحث في جميع الأعمدة المرئية...' : _t('Search in all visible columns...');
|
||||
var clearText = isArabic ? 'مسح' : _t('Clear');
|
||||
var countText = '';
|
||||
if (isFiltered) {
|
||||
countText = isArabic ?
|
||||
'عدد السجلات: ' + savedCount :
|
||||
_t('Found: ') + savedCount + _t(' records');
|
||||
}
|
||||
var countText = isFiltered ?
|
||||
_t('Found: ') + savedCount + _t(' records') : '';
|
||||
|
||||
var html =
|
||||
'<div class="oe_search_container" dir="' + dir + '" style="display: flex; align-items: center; margin: 8px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); padding: 12px; border-radius: 6px; border: 1px solid #dee2e6; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">' +
|
||||
'<input type="text" class="oe_search_input form-control" ' +
|
||||
'placeholder="' + _.escape(placeholder) + '" ' +
|
||||
'<div class="oe_search_container d-flex align-items-center">' +
|
||||
'<input type="text" class="oe_search_input form-control flex-grow-1" ' +
|
||||
'placeholder="' + _.escape(_t('Search in all visible columns...')) + '" ' +
|
||||
'value="' + _.escape(savedValue) + '" ' +
|
||||
'dir="' + dir + '" ' +
|
||||
'style="flex: 1; border: 1px solid #ced4da; height: 36px; padding: 0 12px; border-radius: 4px; font-size: 14px;" ' +
|
||||
'autocomplete="off">' +
|
||||
'<button class="btn btn-secondary oe_clear_search ml-2" style="display: ' + (savedValue ? 'inline-block' : 'none') + '; min-width: 70px;">' +
|
||||
'<i class="fa fa-times mr-1"></i>' + clearText +
|
||||
'<button class="btn btn-secondary oe_clear_search ml-2" style="display: ' +
|
||||
(savedValue ? 'inline-block' : 'none') + ';">' +
|
||||
'<i class="fa fa-times mr-1"></i>' + _t('Clear') +
|
||||
'</button>' +
|
||||
'<span class="oe_search_count badge badge-success ml-2" style="display: ' + (isFiltered ? 'inline-block' : 'none') + '; padding: 6px 10px; font-size: 0.9em;">' +
|
||||
'<span class="oe_search_count badge badge-success ml-2" style="display: ' +
|
||||
(isFiltered ? 'inline-block' : 'none') + ';">' +
|
||||
countText +
|
||||
'</span>' +
|
||||
'<span class="oe_search_loading ml-2" style="display: none; color: #007bff;">' +
|
||||
'<span class="oe_search_loading ml-2" style="display: none;">' +
|
||||
'<i class="fa fa-spinner fa-spin"></i>' +
|
||||
'</span>' +
|
||||
'</div>';
|
||||
|
||||
this.$el.prepend($(html));
|
||||
|
||||
// Store initial value
|
||||
this._lastInputValue = savedValue;
|
||||
|
||||
// Focus on search input if it has value and set cursor to end
|
||||
if (savedValue) {
|
||||
var $input = this.$('.oe_search_input');
|
||||
$input.focus();
|
||||
|
|
@ -666,17 +446,11 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle input events
|
||||
*/
|
||||
_onCustomSearchInput: function(e) {
|
||||
var currentValue = $(e.currentTarget).val();
|
||||
var hasValue = !!currentValue.trim();
|
||||
|
||||
// Only handle if value actually changed (not just cursor movement)
|
||||
if (currentValue === this._lastInputValue) {
|
||||
return;
|
||||
}
|
||||
if (currentValue === this._lastInputValue) return;
|
||||
|
||||
this._lastInputValue = currentValue;
|
||||
this.$('.oe_clear_search').toggle(hasValue);
|
||||
|
|
@ -686,59 +460,30 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle keyup events with debounce
|
||||
*/
|
||||
_onCustomSearchKeyUp: function(e) {
|
||||
var self = this;
|
||||
var value = $(e.currentTarget).val().trim();
|
||||
var ignoreKeys = [13, 27, 16, 17, 18, 91, 93, 37, 38, 39, 40,
|
||||
33, 34, 35, 36, 9, 20, 144, 145,
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123];
|
||||
|
||||
// List of keys to ignore (navigation, modifiers, etc.)
|
||||
var ignoreKeys = [
|
||||
13, // Enter (handled separately)
|
||||
27, // Escape (handled separately)
|
||||
16, 17, 18, // Shift, Ctrl, Alt
|
||||
91, 93, // Command keys (Mac)
|
||||
37, 38, 39, 40, // Arrow keys
|
||||
33, 34, 35, 36, // Page Up/Down, Home, End
|
||||
9, // Tab
|
||||
20, // Caps Lock
|
||||
144, // Num Lock
|
||||
145, // Scroll Lock
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123 // F1-F12
|
||||
];
|
||||
|
||||
// Ignore if it's a navigation or modifier key
|
||||
if (ignoreKeys.indexOf(e.which) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the actual value changed
|
||||
if (value === this._lastSearchValue) {
|
||||
return;
|
||||
}
|
||||
if (ignoreKeys.indexOf(e.which) !== -1) return;
|
||||
if (value === this._lastSearchValue) return;
|
||||
|
||||
this._lastSearchValue = value;
|
||||
|
||||
// Clear previous timer
|
||||
if (this._searchTimer) {
|
||||
clearTimeout(this._searchTimer);
|
||||
}
|
||||
|
||||
// Show loading after short delay
|
||||
this._searchTimer = setTimeout(function() {
|
||||
// Delegate to controller
|
||||
if (self.getParent() && self.getParent()._handleCustomSearch) {
|
||||
self.getParent()._handleCustomSearch(value);
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle special keys
|
||||
*/
|
||||
_onCustomSearchKeyDown: function(e) {
|
||||
// Enter key - trigger search immediately
|
||||
if (e.which === 13) {
|
||||
e.preventDefault();
|
||||
if (this._searchTimer) {
|
||||
|
|
@ -748,26 +493,19 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
if (this.getParent() && this.getParent()._handleCustomSearch) {
|
||||
this.getParent()._handleCustomSearch(value);
|
||||
}
|
||||
}
|
||||
// Escape key - clear search
|
||||
else if (e.which === 27) {
|
||||
} else if (e.which === 27) {
|
||||
e.preventDefault();
|
||||
this._onCustomClearSearch();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle clear button click
|
||||
*/
|
||||
_onCustomClearSearch: function() {
|
||||
// Clear input
|
||||
this.$('.oe_search_input').val('').focus();
|
||||
this.$('.oe_clear_search').hide();
|
||||
this.$('.oe_search_count').hide();
|
||||
this._lastInputValue = '';
|
||||
this._lastSearchValue = '';
|
||||
|
||||
// Delegate to controller
|
||||
if (this.getParent() && this.getParent()._clearCustomSearch) {
|
||||
this.getParent()._clearCustomSearch();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue