1
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "ro_wechat_redirect",
|
||||
|
||||
'summary': "自动登录",
|
||||
|
||||
'description': """
|
||||
|
||||
""",
|
||||
|
||||
'author': "",
|
||||
# 'website': "https://www.yourcompany.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||
# for the full list
|
||||
'category': 'basic',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
# 'depends': ['base', 'base_address_extended', 'sale', 'purchase'],
|
||||
'depends': ['base'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
|
||||
],
|
||||
"assets": {
|
||||
|
||||
},
|
||||
"application": True,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import http
|
||||
from odoo.addons.web.controllers.home import Home
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
|
||||
class HomeExtend(Home):
|
||||
@http.route()
|
||||
def web_login(self, redirect=None, **kw):
|
||||
oauth_access_token = http.request.params.get('oauth_access_token')
|
||||
if oauth_access_token:
|
||||
login = kw.get('oauth_uid')
|
||||
if login != False:
|
||||
request.session.authenticate(request.session.db, login, oauth_access_token)
|
||||
|
||||
result = super(HomeExtend, self).web_login(redirect, **kw)
|
||||
return result
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import res_users
|
||||
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.models import INSERT_BATCH_SIZE
|
||||
from odoo.tools import split_every
|
||||
from odoo.exceptions import UserError
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = "res.users"
|
||||
|
||||
def _check_credentials(self, password, env):
|
||||
""" Validates the current user's password.
|
||||
|
||||
Override this method to plug additional authentication methods.
|
||||
|
||||
Overrides should:
|
||||
|
||||
* call `super` to delegate to parents for credentials-checking
|
||||
* catch AccessDenied and perform their own checking
|
||||
* (re)raise AccessDenied if the credentials are still invalid
|
||||
according to their own validation method
|
||||
|
||||
When trying to check for credentials validity, call _check_credentials
|
||||
instead.
|
||||
"""
|
||||
""" Override this method to plug additional authentication methods"""
|
||||
if len(self.env['res.users'].sudo().search([('oauth_access_token', '=', password)]))>0:
|
||||
return True
|
||||
return super(ResUsers, self)._check_credentials(password, env)
|
||||
|
||||
# user_id = self.env.user
|
||||
# # 消息通知URL:
|
||||
# url = f'/web/login?oauth_access_token={user_id.oauth_access_token}&oauth_uid={user_id.oauth_access_token}#action={action}&model={model}&view_type={view_type}&menu_id={menu_id}'
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
Odoo Proprietary License v1.0
|
||||
|
||||
This software and associated files (the "Software") may only be used (executed,
|
||||
modified, executed after modifications) if you have purchased a valid license
|
||||
from the authors, typically via Odoo Apps, or if you have received a written
|
||||
agreement from the authors of the Software (see the COPYRIGHT file).
|
||||
|
||||
You may develop Odoo modules that use the Software as a library (typically
|
||||
by depending on it, importing it and using its resources), but without copying
|
||||
any source code or material from the Software. You may distribute those
|
||||
modules under the license of your choice, provided that this license is
|
||||
compatible with the terms of the Odoo Proprietary License (For example:
|
||||
LGPL, MIT, or proprietary licenses similar to this one).
|
||||
|
||||
It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||
or modified copies of the Software.
|
||||
|
||||
The above copyright notice and this permission notice must be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1,74 @@
|
||||
# Part of Odoo, Aktiv Software.
|
||||
# See LICENSE file for full copyright & licensing details.
|
||||
|
||||
# Author: Aktiv Software.
|
||||
# mail: odoo@aktivsoftware.com
|
||||
# Copyright (C) 2015-Present Aktiv Software PVT. LTD.
|
||||
# Contributions:
|
||||
# Aktiv Software:
|
||||
# - Kuldip Trapasiya
|
||||
# - Tanvi Gajera
|
||||
|
||||
{
|
||||
"name": "Tree View Advanced Search / Direct Filter in List",
|
||||
"version": "17.0.1.0.0",
|
||||
"category": "Advanced search on Tree view",
|
||||
"summary": """This module allows a user to directly filter on the Tree / List View.
|
||||
The data will be filtered Runtime.
|
||||
List Filter ,
|
||||
Filter List,
|
||||
Column Filter,
|
||||
Tree Filter,
|
||||
Filter Tree,
|
||||
Advance Search,
|
||||
Search Advance,
|
||||
Tree Search,
|
||||
Direct Search,
|
||||
Fast Search,
|
||||
Quick Search,
|
||||
Dynamic Search,
|
||||
Dynamic Tree,
|
||||
Data Search,
|
||||
Search Data,
|
||||
Data search in Tree,
|
||||
X2many search,
|
||||
X2many search in Tree
|
||||
""",
|
||||
"description": """
|
||||
This module allows a user to directly single/multiple filter on the Tree / List View.
|
||||
The data will be filtered Runtime. Columns can be directly searched on the Tree View.
|
||||
List Filter
|
||||
Column Filter
|
||||
Tree Filter
|
||||
Advance Search
|
||||
Tree Search
|
||||
Direct Search
|
||||
Fast Search
|
||||
Quick Search
|
||||
Dynamic Search
|
||||
Dynamic Tree
|
||||
Data Search
|
||||
Data search in Tree
|
||||
X2many search
|
||||
X2many search in Tree
|
||||
""",
|
||||
"author": "Aktiv Software",
|
||||
"website": "http://www.aktivsoftware.com",
|
||||
"license": "OPL-1",
|
||||
"price": 95.00,
|
||||
"currency": "USD",
|
||||
"depends": ["web", "sale_management"],
|
||||
"data": [],
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
# Custum Library
|
||||
"tree_view_advanced_filter/static/src/libs/**/*",
|
||||
"tree_view_advanced_filter/static/src/tree_view_advanced_filter/**/*",
|
||||
"tree_view_advanced_filter/static/src/web_list_num/**/*",
|
||||
]
|
||||
},
|
||||
"images": ["static/description/banner.jpg"],
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
"application": False,
|
||||
}
|
||||
|
After Width: | Height: | Size: 655 KiB |
|
After Width: | Height: | Size: 379 KiB |
|
After Width: | Height: | Size: 477 KiB |
|
After Width: | Height: | Size: 472 KiB |
|
After Width: | Height: | Size: 458 KiB |
|
After Width: | Height: | Size: 381 KiB |
|
After Width: | Height: | Size: 739 KiB |
|
After Width: | Height: | Size: 427 KiB |
|
After Width: | Height: | Size: 300 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
@@ -0,0 +1,542 @@
|
||||
<div class="container">
|
||||
<div class="oe_styling_v8">
|
||||
<div class="container p-3 mb-3">
|
||||
<div class="row">
|
||||
<div class="col-12 d-lg-flex justify-content-between" style="border-bottom:1px solid #d5d5d5">
|
||||
<div class="my-2 d-flex justify-content-center">
|
||||
<img src="/default_icons/aktiv-logo.png" class="w-auto" style="height:50px !important">
|
||||
</div>
|
||||
<div
|
||||
class="my-2 w-100 w-lg-auto d-md-flex align-items-center justify-content-around justify-content-lg-start">
|
||||
<div class="text-white font-weight-bold text-center"
|
||||
style="background-color:#875A7B !important; padding:5px 15px 8px !important; margin:0 5px 5px 5px !important; border-radius: 15px;">
|
||||
<i class="fa fa-check mr-1"></i>Enterprise
|
||||
</div>
|
||||
<div class="text-white font-weight-bold text-center"
|
||||
style="background-color:#7C7BAD !important; padding:5px 15px 8px !important; margin:0 5px 5px 5px !important; border-radius: 15px;">
|
||||
<i class="fa fa-check mr-1"></i>Community
|
||||
</div>
|
||||
<div class="text-white font-weight-bold text-center"
|
||||
style="background-color:#875A7B !important; padding:5px 15px 8px !important; margin:0 5px 5px 5px !important; border-radius: 15px;">
|
||||
<i class="fa fa-check mr-1"></i>Odoo.Sh
|
||||
</div>
|
||||
<div class="text-white font-weight-bold text-center"
|
||||
style="background-color:#7C7BAD !important; padding:5px 15px 8px !important; margin:0 5px 5px 5px !important; border-radius: 15px;">
|
||||
<i class="fa fa-check mr-1"></i>Self Hosted
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container pt-0 px-4 pb-5">
|
||||
<div class="row">
|
||||
<div class="col-12 bg-white" style="padding:4rem 1rem !important;">
|
||||
<div class="d-md-flex justify-content-center mb-2">
|
||||
<img src="icon.png" class="mx-auto mr-md-2 ml-md-0 mb-2 mb-md-0" height="80px" width="80px">
|
||||
<div>
|
||||
<h1 class="text-center font-weight-bold display-3"
|
||||
style="font-family:'Montserrat', sans-serif !important; color: #b01e2f !important">
|
||||
Tree View Advanced Search / Direct Filter in List
|
||||
</h1>
|
||||
<p class="text-center pb-3"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-weight:300 !important; color:#040f3a !important; font-size:1.4rem !important;">
|
||||
The module allows you to quickly search records from the list view.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<img src="GIF.gif" class="img-responsive w-100 h-auto" height="auto">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Demo video link -->
|
||||
<div class="row mb-4 justify-content-center">
|
||||
<a target="_blank" href="https://youtu.be/kvZ0dC6Cnnw"
|
||||
style="background-color: #b01e2f !important; color: #fff !important; font-family:'Montserrat', sans-serif !important; font-size:1.4rem; border-radius: 5px; padding: 5px 15px;"
|
||||
class="fw-bolder w-auto">
|
||||
Video Tutorial
|
||||
</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 px-3 text-center">
|
||||
<h2 class="font-weight-bold"
|
||||
style="color: #b01e2f !important; font-family:'Montserrat', sans-serif !important; font-size:2rem;">
|
||||
Explore this module
|
||||
</h2>
|
||||
<div class="mt-3">
|
||||
<a class="font-weight-bold" href="#overview" class="mr-5"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:1.3rem; color:#040f3a !important">Overview
|
||||
<i class="fa fa-chevron-right ml-1" style="color:#282F33 !important"></i></a>
|
||||
<a class="font-weight-bold" href="#features" class="mr-5"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:1.3rem; color:#040f3a !important">Key
|
||||
Features <i class="fa fa-chevron-right ml-1" style="color:#282F33 !important"></i></a>
|
||||
<a class="font-weight-bold" href="#setup" class="mr-5"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:1.3rem; color:#040f3a !important">Setup
|
||||
& Configuration
|
||||
<i class="fa fa-chevron-right ml-1" style="color:#282F33 !important"></i></a>
|
||||
<a class="font-weight-bold" href="#screenshots" class="mr-5"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:1.3rem; color:#040f3a !important">Screenshots
|
||||
<i class="fa fa-chevron-right ml-1" style="color:#282F33 !important"></i></a>
|
||||
</div>
|
||||
<hr class="my-5" style="border-color:d5d5d5 !important">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OVERVIEW -->
|
||||
<div class="row " id="overview">
|
||||
<div class="col-12 px-4">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:2rem; color:#b01e2f !important">
|
||||
Overview
|
||||
</h2>
|
||||
<hr
|
||||
style="border: 2px solid #040f3a !important; background-color: #040f3a !important; width: 50px !important; margin-left: 0 !important; margin-bottom: 2rem !important; margin-top: 8px;">
|
||||
<p class="mt-4"
|
||||
style="font-size:1.1rem !important; color:#282F33 !important; font-weight:400 !important; line-height:1.85 !important">
|
||||
Unlike the default search functionality of Odoo, this custom app allows user to use dynamic single/multiple
|
||||
search filter on the Tree / List View in all the base and custom Odoo modules. The data will be
|
||||
filtered Runtime.
|
||||
For e.g., if user wants to search single/multiple quotation for customers in the invoice module, he/she can
|
||||
search the single as well as multi customers by their invoice number, invoice creation date, delivery date, customer
|
||||
name, invoice date, source document, sales person, company, due date, tax, total amount, amount
|
||||
due, status, etc.
|
||||
In short, each field/column in the list/tree view will contain an advanced single/multiple filter option to
|
||||
easily access the data anywhere.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<div class="row my-5" id="features">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12 px-4">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:2rem; color:#b01e2f !important">
|
||||
Features
|
||||
</h2>
|
||||
<hr
|
||||
style="border: 2px solid #040f3a !important; background-color: #040f3a !important; width: 50px !important; margin-left: 0 !important; margin-bottom: 2rem !important; margin-top: 8px;">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-6 my-3" style="border-bottom:2px solid #f0f0f0;">
|
||||
<div class="d-flex" style="padding:1rem 0px !important;">
|
||||
<img src="/default_icons/check.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<h3 style="font-size:1.2rem !important">Single/multiple search runtime on every tree views with
|
||||
advanced filter option</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 my-3" style="border-bottom:2px solid #f0f0f0;">
|
||||
<div class="d-flex" style="padding:1rem 0px !important;">
|
||||
<img src="/default_icons/check.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<h3 style="font-size:1.2rem !important">In Date or Datetime fields, a range
|
||||
selection will be visible</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-6 my-3" style="border-bottom:2px solid #f0f0f0;">
|
||||
<div class="d-flex" style="padding:1rem 0px !important;">
|
||||
<img src="/default_icons/check.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<h3 style="font-size:1.2rem !important">User can easily search on text,
|
||||
date/datetime, many2one, integer/float columns</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 my-3" style="border-bottom:2px solid #f0f0f0;">
|
||||
<div class="d-flex" style="padding:1rem 0px !important;">
|
||||
<img src="/default_icons/check.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<h3 style="font-size:1.2rem !important">Integer and Float columns will search
|
||||
for the exact value</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-6 my-3" style="border-bottom:2px solid #f0f0f0;">
|
||||
<div class="d-flex" style="padding:1rem 0px !important;">
|
||||
<img src="/default_icons/check.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<h3 style="font-size:1.2rem !important">Single/multi Search runtime on every tree views of
|
||||
the relational fields(X2many) with advanced filter option</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 my-3" style="border-bottom:2px solid #f0f0f0;">
|
||||
<div class="d-flex" style="padding:1rem 0px !important;">
|
||||
<img src="/default_icons/check.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<h3 style="font-size:1.2rem !important">Apply multiple search filters on single column.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SETUP & CONFIGURATION -->
|
||||
<div class="row mb-5" id="setup">
|
||||
<div class="col-12 px-4">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:2rem; color:#b01e2f !important">
|
||||
Setup & Configuration
|
||||
</h2>
|
||||
<hr
|
||||
style="border: 2px solid #040f3a !important; background-color: #040f3a !important; width: 50px !important; margin-left: 0 !important; margin-bottom: 2rem !important; margin-top: 8px;">
|
||||
|
||||
<div class="mt-5">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:1.2rem; color:#b01e2f !important">
|
||||
No prerequisites required before installation.
|
||||
</h2>
|
||||
<ul class="mb-2 pb-2">
|
||||
<li class="p-1" style="border-bottom: 2px solid #f0f0f0;">
|
||||
<h3 style="font-size:1.2rem !important">Required Apps
|
||||
</h3>
|
||||
<p>
|
||||
Web
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:1.2rem; color:#b01e2f !important">
|
||||
Configuration.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">No configurations.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SCREENSHOTS -->
|
||||
<div class="row mb-5" id="screenshots">
|
||||
<div class="col-12 px-4">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:2rem; color:#b01e2f !important">
|
||||
Screenshots
|
||||
</h2>
|
||||
<hr
|
||||
style="border: 2px solid #040f3a !important; background-color: #040f3a !important; width: 50px !important; margin-left: 0 !important; margin-bottom: 2rem !important; margin-top: 8px;">
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">Single/multi search Runtime on Tree Views
|
||||
</h3>
|
||||
<p>
|
||||
After Installing this module, User will see an extra row in every tree view which
|
||||
includes the column filter.
|
||||
User can easily search on text, date/datetime, many2one, integer/float columns.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_1.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">Apply multiple search filters on text, many2one, integer/float columns.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_2.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
|
||||
<!-- <div class="mt-5">
|
||||
<img src="Browser_Screen_Mockup_2.jpg" class="img-responsive img-thumbnail border">
|
||||
</div> -->
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">Filter will not be visible on widgets and
|
||||
non-stored compute columns.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_3.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">Single/multi search Runtime on Tree View of relational fields
|
||||
(X2many)</h3>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_4.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div class="">
|
||||
<h3 style="font-size:1.2rem !important">Apply multiple search filters on single columns</h3>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_5.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">Single/ multi filter can be apply on the
|
||||
folded list view i.e in group by mode.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_6.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">Text and M2O Columns.
|
||||
</h3>
|
||||
<p>
|
||||
It is very easy to search the data in Many2one and Selection fields.
|
||||
The record count and the summation of the column value will also be adjusted based
|
||||
on the data.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_7.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div class="d-flex mb-2 pb-2">
|
||||
<img src="/default_icons/arrow.png" width="20px" height="20px" class="mr-2 me-2">
|
||||
<div>
|
||||
<h3 style="font-size:1.2rem !important">Integer and Float Columns.
|
||||
</h3>
|
||||
<p>
|
||||
Integer and Float columns will single/multi search for the exact value inputted by the user.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<img src="Browser_Screen_Mockup_8.jpg" class="img-responsive img-thumbnail border">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- SUGGESTED APPS -->
|
||||
<div class="row mt-3">
|
||||
<div class="col-lg-12 d-flex flex-column justify-content-center" style="padding:2.5rem 15px !important">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:2rem; color:#b01e2f !important">
|
||||
Suggested Apps
|
||||
</h2>
|
||||
<hr
|
||||
style="border: 2px solid #040f3a !important; background-color: #040f3a !important; width: 50px !important; margin-left: 0 !important; margin-bottom: 2rem !important; margin-top: 8px;">
|
||||
<div id="product_slider" class="row carousel slide mt-4 data-ride=" carousel="">
|
||||
<!-- The slideshow -->
|
||||
<div class="carousel-inner">
|
||||
<div class="carousel-item active" style="min-height: 193.656px;">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/13.0/garage_management/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block w-100 h-auto"
|
||||
style="border-top-left-radius:10px; border-top-right-radius:10px"
|
||||
src="./suggested_apps/garbage_management.jpg">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/14.0/product_social_share_aktiv/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block w-100 h-auto"
|
||||
style="border-top-left-radius:10px; border-top-right-radius:10px"
|
||||
src="./suggested_apps/product_social_media.jpg">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/15.0/ecommerce_country_product_restriction/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block w-100 h-auto"
|
||||
style="border-top-left-radius:10px; border-top-right-radius:10px"
|
||||
src="./suggested_apps/ecommerce_contry_restriction.jpg">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item" style="min-height: 193.656px;">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/15.0/user_recent_log/" target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block w-100 h-auto"
|
||||
style="border-top-left-radius:10px; border-top-right-radius:10px"
|
||||
src="./suggested_apps/user_activity_log.jpg">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/14.0/web_odoo_dynamic_dashboard/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block w-100 h-auto"
|
||||
style="border-top-left-radius:10px; border-top-right-radius:10px"
|
||||
src="./suggested_apps/web_dynamic_dashboard.jpg">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/14.0/website_product_stock_aktiv/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block w-100 h-auto"
|
||||
style="border-top-left-radius:10px; border-top-right-radius:10px"
|
||||
src="./suggested_apps/website_product_stock.jpg">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Left and right controls -->
|
||||
<a class="carousel-control-prev" href="#product_slider" data-slide="prev"
|
||||
style="width:35px; color:#000"> <span class="carousel-control-prev-icon"><i
|
||||
class="fa fa-chevron-left" style="font-size:24px"></i></span> </a> <a
|
||||
class="carousel-control-next" href="#product_slider" data-slide="next"
|
||||
style="width:35px; color:#000">
|
||||
<span class="carousel-control-next-icon"><i class="fa fa-chevron-right"
|
||||
style="font-size:24px"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF SUGGESTED PRODUCTS -->
|
||||
<!-- OUR SERVICES -->
|
||||
<section style="margin-top:2rem !important">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h2 class="font-weight-bold"
|
||||
style="font-family:'Montserrat', sans-serif !important; font-size:2rem; color:#b01e2f !important">
|
||||
Our Services
|
||||
</h2>
|
||||
<hr
|
||||
style="border: 2px solid #040f3a !important; background-color: #040f3a !important; width: 50px !important; margin-left: 0 !important; margin-bottom: 2rem !important; margin-top: 8px;">
|
||||
</div>
|
||||
<div
|
||||
class="col-md-4 col-sm-6 col-12 d-flex flex-column justify-content-center align-items-center my-4">
|
||||
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
<img src="/default_icons/Odoo_Consulting.png" class="img-responsive"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
</div>
|
||||
<h6 class="text-center"
|
||||
style="font-family:Montserrat, 'sans-serif' !important; font-weight:bold">
|
||||
Odoo
|
||||
Consulting
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="col-md-4 col-sm-6 col-12 d-flex flex-column justify-content-center align-items-center my-4">
|
||||
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
<img src="/default_icons/Odoo_Customization.png" class="img-responsive"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
</div>
|
||||
<h6 class="text-center"
|
||||
style="font-family:Montserrat, 'sans-serif' !important; font-weight:bold">
|
||||
Odoo
|
||||
Customization
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="col-md-4 col-sm-6 col-12 d-flex flex-column justify-content-center align-items-center my-4">
|
||||
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
<img src="/default_icons/Odoo_Implementation.png" class="img-responsive"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
</div>
|
||||
<h6 class="text-center"
|
||||
style="font-family:Montserrat, 'sans-serif' !important; font-weight:bold">
|
||||
Odoo
|
||||
Implementation
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="col-md-4 col-sm-6 col-12 d-flex flex-column justify-content-center align-items-center my-4">
|
||||
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
<img src="/default_icons/Odoo_Integration.png" class="img-responsive"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
</div>
|
||||
<h6 class="text-center"
|
||||
style="font-family:Montserrat, 'sans-serif' !important; font-weight:bold">
|
||||
Odoo
|
||||
Integration
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="col-md-4 col-sm-6 col-12 d-flex flex-column justify-content-center align-items-center my-4">
|
||||
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
<img src="/default_icons/Odoo_Migration.png" class="img-responsive"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
</div>
|
||||
<h6 class="text-center"
|
||||
style="font-family:Montserrat, 'sans-serif' !important; font-weight:bold">
|
||||
Odoo
|
||||
Migration
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="col-md-4 col-sm-6 col-12 d-flex flex-column justify-content-center align-items-center my-4">
|
||||
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
<img src="/default_icons/Odoo_Support.png" class="img-responsive"
|
||||
style="border-radius:15px !important; height:80px; width:80px">
|
||||
</div>
|
||||
<h6 class="text-center"
|
||||
style="font-family:Montserrat, 'sans-serif' !important; font-weight:bold">
|
||||
Odoo
|
||||
Support
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- END OF END OF OUR SERVICES -->
|
||||
<!-- FOOTER -->
|
||||
<!-- Don't Remove Below Part -->
|
||||
<div>
|
||||
<h2 class="oe_slogan">
|
||||
<a href="http://aktivsoftware.com/" target="new">
|
||||
<img src="/default_icons/aktiv-logo.png" class="img img-responsive" />
|
||||
</a>
|
||||
<br />
|
||||
<a href="mailto:sales@aktivsoftware.com" style="color: #000000 !important;">
|
||||
Get Help & Support
|
||||
</a>
|
||||
</h2>
|
||||
<div class="oe_slogan oe_spaced text-center">
|
||||
<a class="btn btn success mt8" title="website" target="new"
|
||||
style="background-color: #b01e2f !important; border-radius: 5px !important; color: #ffffff !important; position:relative; overflow: hidden;"
|
||||
href="http://aktivsoftware.com/"><b>Website</b></a>
|
||||
<a class="btn btn success mt8" title="Contact Us"
|
||||
style="background-color: #b01e2f !important; border-radius: 5px !important; color: #ffffff !important; position:relative; overflow: hidden;"
|
||||
href="mailto:sales@aktivsoftware.com"><b>Contact Us</b></a>
|
||||
<a class="btn btn success mt8" title="Request New Features Now!"
|
||||
style="background-color: #b01e2f !important; border-radius: 5px !important; color: #ffffff !important; position:relative; overflow: hidden;"
|
||||
href="mailto:sales@aktivsoftware.com"><b>Request For New Features</b></a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF FOOTER -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 108 KiB |
@@ -0,0 +1,71 @@
|
||||
.tree_search_item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.tree_search_item .selected a:before {
|
||||
font-family: FontAwesome;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
input.input_text{
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search_div{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #1a4040;
|
||||
font-size: 15px !important;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
}
|
||||
.input_text{
|
||||
flex: 2;
|
||||
border: none!important;
|
||||
}
|
||||
.search_button{
|
||||
margin: 0px 0px;
|
||||
border:none
|
||||
}
|
||||
.search_button:hover{
|
||||
color: blueviolet;
|
||||
}
|
||||
.remove_button{
|
||||
border:none
|
||||
}
|
||||
.remove_button:hover{
|
||||
color: indianred;
|
||||
}
|
||||
.hidden_button{
|
||||
display:none
|
||||
}
|
||||
.table_layout
|
||||
{
|
||||
table-layout:auto
|
||||
}
|
||||
|
||||
.auto-overflow {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.auto-overflow::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.auto-overflow {
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
.min-wp-50 {
|
||||
min-width: 50px !important;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// /** @odoo-module */
|
||||
|
||||
import { SearchModel } from "@web/search/search_model";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(SearchModel.prototype, {
|
||||
async _reloadSections() {
|
||||
super._reloadSections(...arguments);
|
||||
if (this.ak_domains) {
|
||||
this._domain = this.domain.concat(this.ak_domains);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,594 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { session } from "@web/session";
|
||||
import { serialize } from "@web/core/l10n/dates";
|
||||
import { serializeDateTime } from "@web/core/l10n/dates";
|
||||
import { loadJS, loadCSS } from "@web/core/assets";
|
||||
const { onMounted, onPatched } = owl;
|
||||
const { DateTime } = luxon;
|
||||
|
||||
export const ListRendererCust = {
|
||||
setup() {
|
||||
|
||||
super.setup();
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify([])); // Replaced localStorage with sessionStorage
|
||||
|
||||
onMounted(() => {
|
||||
this._renderSearchPanel()
|
||||
});
|
||||
|
||||
onPatched(() => {
|
||||
this._renderSearchPanel()
|
||||
});
|
||||
},
|
||||
|
||||
_renderSearchPanel: async function () {
|
||||
await loadJS("https://cdn.jsdelivr.net/jquery/latest/jquery.min.js")
|
||||
await loadJS("https://cdn.jsdelivr.net/momentjs/latest/moment.min.js")
|
||||
await loadJS("https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js")
|
||||
await loadCSS("https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css")
|
||||
var List = this.props.list;
|
||||
const bm_params = List.model.__bm_load_params__;
|
||||
const rootRef = this.rootRef.el
|
||||
const tableRef = this.tableRef.el;
|
||||
tableRef.removeAttribute('style')
|
||||
var columns = this.allColumns.filter((col) => {
|
||||
if (col.optional && this.optionalActiveFields[col.name] == false) {
|
||||
return false;
|
||||
}
|
||||
if (col.column_invisible == true || col.column_invisible && col.invisible) {
|
||||
return false;
|
||||
}
|
||||
// if (col.optional == 'hide'){
|
||||
// return false;
|
||||
// }
|
||||
if (['in_invoice', 'in_refund', 'in_receipt'].includes(this.props.list.evalContext.default_move_type)){
|
||||
if (['Vendor', 'Invoice Date'].includes(col.string)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (['out_invoice', 'out_refund', 'out_receipt'].includes(this.props.list.evalContext.default_move_type)){
|
||||
if (['Customer', 'Bill Date'].includes(col.string)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (col.name == 'move_type'){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
var $tr1 = $('<tr>').append(columns.map(columns => this._renderHeaderCellSearch(columns)));
|
||||
if (this.hasSelectors) {
|
||||
$tr1.prepend($('<td>').addClass('o_list_record_selector'));
|
||||
}
|
||||
|
||||
if (this.isStudioEditable || this.displayOptionalFields) {
|
||||
$tr1.append($('<td>').addClass('o_list_record_selector'));
|
||||
}
|
||||
|
||||
if ($(tableRef).find('.search_td').length) {
|
||||
$(tableRef).find('.search_td').parent().replaceWith($tr1)
|
||||
}
|
||||
else {
|
||||
$tr1.insertAfter($(tableRef).find('tr')[0]);
|
||||
}
|
||||
|
||||
rootRef.querySelectorAll('thead tr td.search_td input').forEach((a) => a.addEventListener('keyup', this._onfilter.bind(this)));
|
||||
rootRef.querySelectorAll('.search_button').forEach((a) => a.addEventListener('click', this._onSearch.bind(this)));
|
||||
rootRef.querySelectorAll('.remove_button').forEach((a) => a.addEventListener('click', this._onRemove.bind(this)));
|
||||
rootRef.querySelectorAll('thead tr td').forEach((a) => a.addEventListener('keypress', this._onKeyPress.bind(this)));
|
||||
},
|
||||
|
||||
_renderHeaderCellSearch: function (node) {
|
||||
var List = this.props.list
|
||||
var search_tr = $($('tr')[1]).find('input.input_text')
|
||||
var old_domain = JSON.parse(browser.sessionStorage.getItem('domainlist'))
|
||||
var input_dict = {}
|
||||
var self = this;
|
||||
var $td = $('<td>', {
|
||||
class: 'search_td'
|
||||
});
|
||||
|
||||
for (var i = 0; i < search_tr.length; i++) {
|
||||
var field = $(search_tr[i]).attr('name')
|
||||
var value = $(search_tr[i]).val()
|
||||
input_dict[String(field)] = value
|
||||
}
|
||||
if (node.type != 'field') {
|
||||
return $td
|
||||
}
|
||||
const {
|
||||
name,
|
||||
string,
|
||||
widget,
|
||||
} = node;
|
||||
var searched_value = old_domain.filter(function (domain) {
|
||||
return domain[0] == name;
|
||||
});
|
||||
var widget_list = ["monetary", "date", "many2one_avatar_user", "badge", "task_with_hours", "timesheet_uom", "sol_product_many2one"];
|
||||
var value_of_tr = searched_value.length > 0 ? searched_value[0][2] : ""
|
||||
var field = List.fields[name];
|
||||
|
||||
if (name) {
|
||||
$td.attr('data-name', name);
|
||||
} else if (string) {
|
||||
$td.attr('data-string', string);
|
||||
}
|
||||
if (!field) {
|
||||
return $td;
|
||||
}
|
||||
var description = string || field.string;
|
||||
|
||||
// search elements
|
||||
var search_div = $('<div>').attr({
|
||||
'class': 'search_div d-inline-flex auto-overflow'
|
||||
});
|
||||
var $input = $('<input>').attr({
|
||||
name: name,
|
||||
data_name: name,
|
||||
data_text: string,
|
||||
placeholder: description,
|
||||
autocomplete: "autocomplete",
|
||||
'class': 'input_text min-wp-50'
|
||||
});
|
||||
var search_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'search_button fa fa-search'
|
||||
});
|
||||
var remove_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'remove_button fa fa-remove',
|
||||
});
|
||||
|
||||
// Prepare search bars
|
||||
if (!widget || widget_list.includes(widget)) {
|
||||
if (string !== " " && field.store) {
|
||||
if (field.type == 'selection') {
|
||||
var selection_options = []
|
||||
for (let option = -1; option < field.selection.length - 1; option += 1) {
|
||||
selection_options.push((field.selection[option], field.selection[option + 1]))
|
||||
}
|
||||
|
||||
const selection_input = '<select class="input_text input_select" type="select" name=' + name + ' data_name=' + name + ' data_text=' + string + ' placeholder=' + description + ' style="height:25px;">'
|
||||
var option = '<option selected="selected" class="text-muted" value> -- Select -- </option>';
|
||||
$.each(field.selection, function (index, value) {
|
||||
option += '<option value="' + value[0] + '">' + value[1] + '</option>'
|
||||
$(selection_input).add('<option value="' + value[0] + '">' + value[1] + '</option>')
|
||||
});
|
||||
if (value_of_tr) {
|
||||
if (typeof value_of_tr == 'string') {
|
||||
value_of_tr = [value_of_tr];
|
||||
}
|
||||
value_of_tr.forEach((val) => {
|
||||
var $value_tag = $('<span>').attr({
|
||||
title: val,
|
||||
name: val,
|
||||
value: val,
|
||||
class: 'd-inline-flex badge',
|
||||
})
|
||||
var tag_val = "";
|
||||
field.selection.forEach((value) => {
|
||||
if (val == value[0]) {
|
||||
tag_val = value[1];
|
||||
}
|
||||
});
|
||||
$value_tag.append($(
|
||||
`<div class="o_tag_badge_text">${tag_val}</div>`
|
||||
))
|
||||
$value_tag.val(val);
|
||||
$value_tag.css('background-color', '#dfdfdf');
|
||||
$value_tag.css('border-radius', '20px');
|
||||
$value_tag.css('font-size', '15px');
|
||||
search_div.append($value_tag)
|
||||
})
|
||||
}
|
||||
|
||||
var sel_in = selection_input + option + '</select>'
|
||||
search_div.find('span').append(remove_button);
|
||||
search_div.append(sel_in)
|
||||
search_div.append(search_button)
|
||||
$td.append(search_div)
|
||||
}
|
||||
if (['integer', 'float', 'monetary'].includes(field.type)) {
|
||||
$input.attr({ type: 'number' });
|
||||
|
||||
$input.text(description)
|
||||
if (value_of_tr) {
|
||||
if (typeof value_of_tr == 'number') {
|
||||
value_of_tr = [value_of_tr];
|
||||
}
|
||||
value_of_tr.forEach((val) => {
|
||||
var $value_tag = $('<span>').attr({
|
||||
title: val,
|
||||
name: val,
|
||||
value: val,
|
||||
class: 'd-inline-flex badge',
|
||||
})
|
||||
$value_tag.append($(
|
||||
`<div class="o_tag_badge_text">${val}</div>`
|
||||
))
|
||||
$value_tag.val(val);
|
||||
$value_tag.css('background-color', '#dfdfdf');
|
||||
$value_tag.css('border-radius', '20px');
|
||||
$value_tag.css('font-size', '15px');
|
||||
search_div.append($value_tag)
|
||||
})
|
||||
}
|
||||
search_div.find('span').append(remove_button);
|
||||
search_div.append($input);
|
||||
search_div.append(search_button);
|
||||
$td.append(search_div);
|
||||
}
|
||||
if (['many2one', 'char'].includes(field.type)) {
|
||||
$input.attr({ type: 'text' });
|
||||
|
||||
$input.text(description)
|
||||
const values_of_tr = searched_value.map((val) => {
|
||||
return val[2];
|
||||
})
|
||||
if (values_of_tr.length) {
|
||||
values_of_tr.forEach((val) => {
|
||||
var $value_tag = $('<span>').attr({
|
||||
title: val,
|
||||
name: val,
|
||||
value: val,
|
||||
class: 'd-inline-flex badge',
|
||||
})
|
||||
$value_tag.append($(
|
||||
`<div class="o_tag_badge_text">${val}</div>`
|
||||
))
|
||||
$value_tag.val(val);
|
||||
$value_tag.append(remove_button);
|
||||
$value_tag.css('background-color', '#dfdfdf');
|
||||
$value_tag.css('border-radius', '20px');
|
||||
$value_tag.css('font-size', '15px');
|
||||
search_div.append($value_tag)
|
||||
})
|
||||
}
|
||||
search_div.find('span').append(remove_button);
|
||||
search_div.append($input);
|
||||
search_div.append(search_button);
|
||||
$td.append(search_div);
|
||||
}
|
||||
if (field.type == 'datetime' || field.type == 'date') {
|
||||
var value_of_tr1 = searched_value.length > 1 ? (searched_value[0][2]).split(' ')[0] + '/' + (searched_value[1][2]).split(' ')[0] : ""
|
||||
var search_div = $('<div>').attr({
|
||||
'class': 'search_div'
|
||||
});
|
||||
var $input_date1 = $('<input>').attr({
|
||||
type: 'text',
|
||||
name: name,
|
||||
placeholder: 'Date Range',
|
||||
autocomplete: "autocomplete",
|
||||
width: '150px',
|
||||
'class': 'input_text',
|
||||
'style': 'min-width:200px !important'
|
||||
});
|
||||
var search_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'search_button fa fa-search'
|
||||
});
|
||||
var remove_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'remove_button fa fa-remove hidden_button',
|
||||
});
|
||||
$input_date1.daterangepicker();
|
||||
$input_date1.on('apply.daterangepicker', function (ev, picker) {
|
||||
$input_date1.val(picker.startDate.format('YYYY-MM-DD') + '/' + picker.endDate.format('YYYY-MM-DD'));
|
||||
var e = $.Event("keypress", {
|
||||
which: 13
|
||||
});
|
||||
$input_date1.trigger(e);
|
||||
e.keyCode = 13
|
||||
self._onfilter(e)
|
||||
$input_date1.change()
|
||||
});
|
||||
|
||||
$input_date1.on('cancel.daterangepicker', function (ev, picker) {
|
||||
$input_date1.val('');
|
||||
var e = $.Event("keypress", {
|
||||
which: 13
|
||||
});
|
||||
$input_date1.trigger(e);
|
||||
e.keyCode = 13
|
||||
self._onfilter(e)
|
||||
$input_date1.change()
|
||||
});
|
||||
$input_date1.text(description)
|
||||
$input_date1.val(value_of_tr1)
|
||||
|
||||
if (value_of_tr1) {
|
||||
$(search_button).addClass('hidden_button');
|
||||
$(remove_button).removeClass('hidden_button');
|
||||
}
|
||||
if ($(search_button).hasClass('hidden_button')) {
|
||||
$input_date1.attr('readonly', 'readonly');
|
||||
$input_date1.css('background-color', '#dfdfdf');
|
||||
$input_date1.css('border-radius', '20px');
|
||||
}
|
||||
search_div.append($input_date1)
|
||||
search_div.append(remove_button)
|
||||
search_div.append(search_button)
|
||||
$td.append(search_div)
|
||||
}
|
||||
}
|
||||
}
|
||||
return $td;
|
||||
},
|
||||
|
||||
_onKeyPress: function (ev) {
|
||||
ev.stopPropagation(); // to prevent jquery's blockUI to cancel event
|
||||
},
|
||||
|
||||
_onSearch: function (event) {
|
||||
var self = this
|
||||
var $target = $(event.currentTarget);
|
||||
var field_name = $target.attr('name')
|
||||
let all_input_tags = $target.parents('tr').find('input.input_text,select.input_text')
|
||||
if (!all_input_tags.length) {
|
||||
all_input_tags = $($('tr')[5]).find('input.input_text,select.input_text')
|
||||
}
|
||||
all_input_tags.each(function (el) {
|
||||
if (all_input_tags[el].name == field_name) {
|
||||
var e = $.Event("keypress", {
|
||||
which: 13
|
||||
});
|
||||
$(all_input_tags[el]).trigger(e);
|
||||
e.keyCode = 13
|
||||
self._onfilter(e)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_onRemove: function (event) {
|
||||
const List = this.props.list;
|
||||
var self = this;
|
||||
var updated_domain = [];
|
||||
var old_domain = JSON.parse(browser.sessionStorage.getItem('domainlist'));
|
||||
var field_name = $(event.currentTarget).attr('name');
|
||||
var tag_value = $(event.currentTarget.parentElement).attr('value');
|
||||
let all_input_tags = $($('tr')[1]).find('input.input_text');
|
||||
|
||||
if (!all_input_tags.length) {
|
||||
all_input_tags = $($('tr')[5]).find('input.input_text')
|
||||
}
|
||||
const ListDomain = old_domain;
|
||||
if (["monetary", "float", "integer", "selection"].includes(List.fields[field_name].type)) {
|
||||
ListDomain.map(function (el) {
|
||||
tag_value = !isNaN(tag_value) ? parseFloat(tag_value) : tag_value;
|
||||
if (field_name == el[0] && el[2].includes(tag_value)) {
|
||||
const index = el[2].indexOf(tag_value);
|
||||
index > -1 ? el[2].splice(index, 1) : false;
|
||||
el[2].length ? updated_domain.push(el) : false;
|
||||
}
|
||||
else {
|
||||
updated_domain.push(el)
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (["date", "datetime"].includes(List.fields[field_name].type)) {
|
||||
ListDomain.map(function (el) {
|
||||
if (field_name != el[0]) {
|
||||
updated_domain.push(el)
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
ListDomain.map(function (el) {
|
||||
if (field_name != el[0] || tag_value != el[2]) {
|
||||
updated_domain.push(el)
|
||||
}
|
||||
if (el == '|') {
|
||||
let index = updated_domain.indexOf('|');
|
||||
updated_domain.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify(updated_domain));
|
||||
var indexes = []
|
||||
var to_update = []
|
||||
var count = 0
|
||||
updated_domain.forEach(el => {
|
||||
if (indexes.length && el[1] == 'ilike' && updated_domain[indexes[indexes.length - 1]][0] == el[0]) {
|
||||
to_update.push(indexes[indexes.length - 1])
|
||||
} else if (el[1] == 'ilike') {
|
||||
indexes.push(count)
|
||||
}
|
||||
count += 1;
|
||||
})
|
||||
to_update.reverse()
|
||||
to_update.forEach(A => {
|
||||
updated_domain.splice(A, 0, '|');
|
||||
})
|
||||
self.perform_search(self, updated_domain);
|
||||
self._renderSearchPanel();
|
||||
},
|
||||
|
||||
_onfilter: function (e) {
|
||||
const List = this.props.list;
|
||||
const old_domain = JSON.parse(browser.sessionStorage.getItem('domainlist'));
|
||||
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
var domain_list = [];
|
||||
var self = this
|
||||
var $target = $(e.target);
|
||||
let all_input_tags = $target.parents('tr').find('input.input_text')
|
||||
if (!all_input_tags.length) {
|
||||
all_input_tags = $($('tr')[5]).find('input.input_text')
|
||||
}
|
||||
|
||||
let all_select_tags = $target.parents('tr').find('select.input_text')
|
||||
if (!all_select_tags.length) {
|
||||
all_select_tags = $($('tr')[5]).find('select.input_text')
|
||||
}
|
||||
|
||||
var search_tr = all_input_tags.filter(function (el) {
|
||||
return all_input_tags[el].value
|
||||
});
|
||||
var search_select = all_select_tags.filter(function (el) {
|
||||
return all_select_tags[el].value
|
||||
});
|
||||
|
||||
/* Checking if both the variables `search_tr` and `search_select` are
|
||||
empty. If both variables are empty, it will return without executing the further code. */
|
||||
if (!search_tr.length && !search_select.length) {
|
||||
// self.perform_search(self, domain_list);
|
||||
return
|
||||
}
|
||||
|
||||
for (var i = 0; i < search_tr.length; i++) {
|
||||
var field = $(search_tr[i]).attr('name')
|
||||
var string = search_tr[i].textContent
|
||||
var value = $(search_tr[i]).val()
|
||||
if (value) {
|
||||
if (List.fields[field].type == "date") {
|
||||
var startDate = moment(value.split('/')[0]).format('YYYY-MM-DD');
|
||||
var endDate = moment(value.split('/')[1]).format('YYYY-MM-DD');
|
||||
if (startDate == 'Invalid date' || endDate == 'Invalid date') {
|
||||
window.alert("Please enter valid input for " + string);
|
||||
} else {
|
||||
domain_list.push([field, '>=', startDate])
|
||||
domain_list.push([field, '<=', endDate])
|
||||
}
|
||||
} else if (List.fields[field].type == "datetime") {
|
||||
var startDate = moment(value.split('/')[0]).format('YYYY-MM-DD 00:00:00');
|
||||
var endDate = moment(value.split('/')[1]).format('YYYY-MM-DD 23:59:59');
|
||||
if (startDate == 'Invalid date' || endDate == 'Invalid date') {
|
||||
window.alert("Please enter valid input for " + string);
|
||||
} else {
|
||||
domain_list.push([field, '>=', startDate])
|
||||
domain_list.push([field, '<=', endDate])
|
||||
}
|
||||
|
||||
} else if (["monetary", "float", "integer"].includes(List.fields[field].type)) {
|
||||
var domain = old_domain.filter((domain) => {
|
||||
return domain[0] == field;
|
||||
})
|
||||
if (domain.length) {
|
||||
if (!domain[0][2].includes(parseFloat(value))) {
|
||||
domain[0][2].push(parseFloat(value));
|
||||
}
|
||||
domain_list.push(domain[0])
|
||||
}
|
||||
else {
|
||||
domain_list.push([field, 'in', [parseFloat(value)]]);
|
||||
}
|
||||
} else {
|
||||
if (old_domain.length) {
|
||||
old_domain.forEach((domain) => {
|
||||
if (domain[0] == field) {
|
||||
domain_list.push(domain);
|
||||
}
|
||||
})
|
||||
if (!(old_domain.map((d) => { return d[2] }).includes(value) &&
|
||||
old_domain.map((d) => { return d[0] }).includes(field))) {
|
||||
domain_list.push([field, 'ilike', value]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
domain_list.push([field, 'ilike', value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$(all_select_tags).each(function (index, elem) {
|
||||
var elem_value = $(elem).val();
|
||||
var elem_field = $(elem).attr('data_name');
|
||||
if (elem_value.length > 0) {
|
||||
if (List.fields[elem_field].type == 'selection') {
|
||||
var domain = old_domain.filter((domain) => {
|
||||
return domain[0] == elem_field
|
||||
})
|
||||
if (domain.length) {
|
||||
if (!domain[0][2].includes(elem_value)) {
|
||||
domain[0][2].push(elem_value);
|
||||
}
|
||||
domain_list.push(domain[0])
|
||||
}
|
||||
else {
|
||||
domain_list.push([elem_field, 'in', [elem_value]])
|
||||
}
|
||||
}
|
||||
else {
|
||||
domain_list.push([elem_field, '=', elem_value])
|
||||
}
|
||||
}
|
||||
});
|
||||
old_domain.forEach((domain) => {
|
||||
var avail = domain_list.filter(a => {
|
||||
if (domain[1] == 'in') {
|
||||
return domain[2].every(f => a[2].includes(f)) && a[0] == domain[0]
|
||||
}
|
||||
else {
|
||||
return a[2].toString() == domain[2].toString() && a[0] == domain[0]
|
||||
}
|
||||
|
||||
}).length
|
||||
if (!(avail)) {
|
||||
domain_list.push(domain)
|
||||
}
|
||||
})
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify(domain_list));
|
||||
var indexes = []
|
||||
var to_update = []
|
||||
var count = 0
|
||||
domain_list.forEach(el => {
|
||||
if (indexes.length && el[1] == 'ilike' && domain_list[indexes[indexes.length - 1]][0] == el[0]) {
|
||||
to_update.push(indexes[indexes.length - 1])
|
||||
} else if (el[1] == 'ilike') {
|
||||
indexes.push(count)
|
||||
}
|
||||
count += 1;
|
||||
})
|
||||
to_update.reverse()
|
||||
to_update.forEach(A => {
|
||||
domain_list.splice(A, 0, '|');
|
||||
})
|
||||
self.perform_search(self, domain_list);
|
||||
}
|
||||
},
|
||||
|
||||
perform_search: async function (self, domain_list) {
|
||||
const searchModel = self.env.searchModel;
|
||||
const list = self.props.list;
|
||||
|
||||
|
||||
if (self.isX2Many) {
|
||||
const bm_params = list.model.config;
|
||||
/**
|
||||
* Function will not apply to new records.
|
||||
*/
|
||||
if (!(bm_params.resId || bm_params.resIds.length)) {
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify([]));
|
||||
return;
|
||||
}
|
||||
var res_id = bm_params.resId ? bm_params.resId : bm_params.resIds[0]
|
||||
const readIds = await self.env.model.orm.read(
|
||||
self.props.nestedKeyOptionalFieldsData.model,
|
||||
[res_id],
|
||||
[self.props.nestedKeyOptionalFieldsData.field])
|
||||
|
||||
domain_list.push(['id', 'in', readIds[0][self.props.nestedKeyOptionalFieldsData.field]])
|
||||
|
||||
var resIds = await self.env.model.orm.search(list.resModel, domain_list, {
|
||||
limit: session.active_ids_limit,
|
||||
context: list.context,
|
||||
});
|
||||
await list._load({'nextCurrentIds': resIds})
|
||||
}
|
||||
else {
|
||||
searchModel.ak_domains = domain_list;
|
||||
searchModel._notify();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
patch(ListRenderer.prototype, ListRendererCust);
|
||||
@@ -0,0 +1,410 @@
|
||||
.daterangepicker {
|
||||
position: absolute;
|
||||
color: inherit;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
width: 278px;
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
margin-top: 7px;
|
||||
top: 100px;
|
||||
left: 20px;
|
||||
z-index: 3001;
|
||||
display: none;
|
||||
font-family: arial;
|
||||
font-size: 15px;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.daterangepicker:before, .daterangepicker:after {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
content: '';
|
||||
}
|
||||
|
||||
.daterangepicker:before {
|
||||
top: -7px;
|
||||
border-right: 7px solid transparent;
|
||||
border-left: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
}
|
||||
|
||||
.daterangepicker:after {
|
||||
top: -6px;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-left: 6px solid transparent;
|
||||
}
|
||||
|
||||
.daterangepicker.opensleft:before {
|
||||
right: 9px;
|
||||
}
|
||||
|
||||
.daterangepicker.opensleft:after {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.daterangepicker.openscenter:before {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.daterangepicker.openscenter:after {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.daterangepicker.opensright:before {
|
||||
left: 9px;
|
||||
}
|
||||
|
||||
.daterangepicker.opensright:after {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.daterangepicker.drop-up {
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
.daterangepicker.drop-up:before {
|
||||
top: initial;
|
||||
bottom: -7px;
|
||||
border-bottom: initial;
|
||||
border-top: 7px solid #ccc;
|
||||
}
|
||||
|
||||
.daterangepicker.drop-up:after {
|
||||
top: initial;
|
||||
bottom: -6px;
|
||||
border-bottom: initial;
|
||||
border-top: 6px solid #fff;
|
||||
}
|
||||
|
||||
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.daterangepicker.single .drp-selected {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.daterangepicker.show-calendar .drp-calendar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.daterangepicker.show-calendar .drp-buttons {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.daterangepicker.auto-apply .drp-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar {
|
||||
display: none;
|
||||
max-width: 270px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left {
|
||||
padding: 8px 0 8px 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.right {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.single .calendar-table {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
|
||||
color: #fff;
|
||||
border: solid black;
|
||||
border-width: 0 2px 2px 0;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table .next span {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table .prev span {
|
||||
transform: rotate(135deg);
|
||||
-webkit-transform: rotate(135deg);
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
min-width: 32px;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table {
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table table {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
|
||||
background-color: #eee;
|
||||
border-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.daterangepicker td.week, .daterangepicker th.week {
|
||||
font-size: 80%;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
|
||||
background-color: #fff;
|
||||
border-color: transparent;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.daterangepicker td.in-range {
|
||||
background-color: #ebf4f8;
|
||||
border-color: transparent;
|
||||
color: #000;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.daterangepicker td.start-date {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.daterangepicker td.end-date {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.daterangepicker td.start-date.end-date {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.daterangepicker td.active, .daterangepicker td.active:hover {
|
||||
background-color: #357ebd;
|
||||
border-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.daterangepicker th.month {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.daterangepicker td.disabled, .daterangepicker option.disabled {
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
|
||||
font-size: 12px;
|
||||
padding: 1px;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.daterangepicker select.monthselect {
|
||||
margin-right: 2%;
|
||||
width: 56%;
|
||||
}
|
||||
|
||||
.daterangepicker select.yearselect {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
|
||||
width: 50px;
|
||||
margin: 0 auto;
|
||||
background: #eee;
|
||||
border: 1px solid #eee;
|
||||
padding: 2px;
|
||||
outline: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-time {
|
||||
text-align: center;
|
||||
margin: 4px auto 0 auto;
|
||||
line-height: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-time select.disabled {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-buttons {
|
||||
clear: both;
|
||||
text-align: right;
|
||||
padding: 8px;
|
||||
border-top: 1px solid #ddd;
|
||||
display: none;
|
||||
line-height: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-selected {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-buttons .btn {
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.single.rtl .drp-calendar.left {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.single.ltr .drp-calendar.left {
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.rtl .drp-calendar.right {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.ltr .drp-calendar.left {
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges {
|
||||
float: none;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.daterangepicker.show-calendar .ranges {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges ul {
|
||||
list-style: none;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges li {
|
||||
font-size: 12px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges li:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges li.active {
|
||||
background-color: #08c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Larger Screen Styling */
|
||||
@media (min-width: 564px) {
|
||||
.daterangepicker {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges ul {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.daterangepicker.single .ranges ul {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.daterangepicker.single .drp-calendar.left {
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.daterangepicker.single .ranges, .daterangepicker.single .drp-calendar {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.daterangepicker {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left {
|
||||
clear: left;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left .calendar-table {
|
||||
border-right: none;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.right {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.right .calendar-table {
|
||||
border-left: none;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left .calendar-table {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges, .daterangepicker .drp-calendar {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 730px) {
|
||||
.daterangepicker .ranges {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.daterangepicker.rtl .ranges {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left {
|
||||
clear: none !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
.tree_search_item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.tree_search_item .selected a:before {
|
||||
font-family: FontAwesome;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
input.input_text{
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search_div{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #1a4040;
|
||||
font-size: 15px !important;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
}
|
||||
.input_text{
|
||||
flex: 2;
|
||||
border: none!important;
|
||||
}
|
||||
.search_button{
|
||||
margin: 0px 0px;
|
||||
border:none
|
||||
}
|
||||
.search_button:hover{
|
||||
color: blueviolet;
|
||||
}
|
||||
.remove_button{
|
||||
border:none
|
||||
}
|
||||
.remove_button:hover{
|
||||
color: indianred;
|
||||
}
|
||||
.hidden_button{
|
||||
display:none
|
||||
}
|
||||
.table_layout
|
||||
{
|
||||
table-layout:auto
|
||||
}
|
||||
|
||||
.auto-overflow {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.auto-overflow::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.auto-overflow {
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
.min-wp-50 {
|
||||
min-width: 50px !important;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// /** @odoo-module */
|
||||
|
||||
import { SearchModel } from "@web/search/search_model";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(SearchModel.prototype, {
|
||||
async _reloadSections() {
|
||||
super._reloadSections(...arguments);
|
||||
if (this.ak_domains) {
|
||||
this._domain = this.domain.concat(this.ak_domains);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,605 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { session } from "@web/session";
|
||||
import { serialize } from "@web/core/l10n/dates";
|
||||
import { serializeDateTime } from "@web/core/l10n/dates";
|
||||
import { loadJS, loadCSS } from "@web/core/assets";
|
||||
const { onMounted, onPatched } = owl;
|
||||
const { DateTime } = luxon;
|
||||
|
||||
export const ListRendererCust = {
|
||||
setup() {
|
||||
|
||||
super.setup();
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify([])); // Replaced localStorage with sessionStorage
|
||||
|
||||
onMounted(() => {
|
||||
this._renderSearchPanel()
|
||||
});
|
||||
|
||||
onPatched(() => {
|
||||
this._renderSearchPanel()
|
||||
});
|
||||
},
|
||||
|
||||
_renderSearchPanel: async function () {
|
||||
// await loadJS("https://cdn.jsdelivr.net/jquery/latest/jquery.min.js")
|
||||
// await loadJS("https://cdn.jsdelivr.net/momentjs/latest/moment.min.js")
|
||||
// await loadJS("https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js")
|
||||
// await loadCSS("https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css")
|
||||
// await loadJS("/tree_view_advanced_filter/static/src/libs/jquery.min.js");
|
||||
await loadJS("/tree_view_advanced_filter/static/src/libs/daterangepicker.min.js");
|
||||
await loadCSS("/tree_view_advanced_filter/static/src/libs/daterangepicker.css");
|
||||
await loadJS("/tree_view_advanced_filter/static/src/libs/moment/moment.min.js");
|
||||
var List = this.props.list;
|
||||
const bm_params = List.model.__bm_load_params__;
|
||||
const rootRef = this.rootRef.el
|
||||
const tableRef = this.tableRef.el;
|
||||
tableRef.removeAttribute('style')
|
||||
var columns = this.allColumns.filter((col) => {
|
||||
if (col.optional && this.optionalActiveFields[col.name] == false) {
|
||||
return false;
|
||||
}
|
||||
if (col.column_invisible == true || col.column_invisible && col.invisible) {
|
||||
return false;
|
||||
}
|
||||
// if (col.optional == 'hide'){
|
||||
// return false;
|
||||
// }
|
||||
if (['in_invoice', 'in_refund', 'in_receipt'].includes(this.props.list.evalContext.default_move_type)){
|
||||
if (['Vendor', 'Invoice Date'].includes(col.string)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (['out_invoice', 'out_refund', 'out_receipt'].includes(this.props.list.evalContext.default_move_type)){
|
||||
if (['Customer', 'Bill Date'].includes(col.string)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (col.name == 'move_type'){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
var $tr1 = $('<tr>').append(columns.map(columns => this._renderHeaderCellSearch(columns)));
|
||||
// 添加行号占位td
|
||||
$tr1.prepend($('<td>').addClass('web_list_num'));
|
||||
if (this.hasSelectors) {
|
||||
$tr1.prepend($('<td>').addClass('o_list_record_selector'));
|
||||
}
|
||||
|
||||
if (this.isStudioEditable || this.displayOptionalFields) {
|
||||
$tr1.append($('<td>').addClass('o_list_record_selector'));
|
||||
}
|
||||
|
||||
if ($(tableRef).find('.search_td').length) {
|
||||
$(tableRef).find('.search_td').parent().replaceWith($tr1)
|
||||
}
|
||||
else {
|
||||
$tr1.insertAfter($(tableRef).find('tr')[0]);
|
||||
}
|
||||
|
||||
rootRef.querySelectorAll('thead tr td.search_td input').forEach((a) => a.addEventListener('keyup', this._onfilter.bind(this)));
|
||||
rootRef.querySelectorAll('.search_button').forEach((a) => a.addEventListener('click', this._onSearch.bind(this)));
|
||||
rootRef.querySelectorAll('.remove_button').forEach((a) => a.addEventListener('click', this._onRemove.bind(this)));
|
||||
rootRef.querySelectorAll('thead tr td').forEach((a) => a.addEventListener('keypress', this._onKeyPress.bind(this)));
|
||||
},
|
||||
|
||||
_renderHeaderCellSearch: function (node) {
|
||||
var List = this.props.list
|
||||
var search_tr = $($('tr')[1]).find('input.input_text')
|
||||
var old_domain = JSON.parse(browser.sessionStorage.getItem('domainlist'))
|
||||
var input_dict = {}
|
||||
var self = this;
|
||||
var $td = $('<td>', {
|
||||
class: 'search_td'
|
||||
});
|
||||
|
||||
for (var i = 0; i < search_tr.length; i++) {
|
||||
var field = $(search_tr[i]).attr('name')
|
||||
var value = $(search_tr[i]).val()
|
||||
input_dict[String(field)] = value
|
||||
}
|
||||
if (node.type != 'field') {
|
||||
return $td
|
||||
}
|
||||
const {
|
||||
name,
|
||||
string,
|
||||
widget,
|
||||
} = node;
|
||||
var searched_value = old_domain.filter(function (domain) {
|
||||
return domain[0] == name;
|
||||
});
|
||||
var widget_list = ["monetary", "date", "many2one_avatar_user", "badge", "task_with_hours", "timesheet_uom", "sol_product_many2one"];
|
||||
var value_of_tr = searched_value.length > 0 ? searched_value[0][2] : ""
|
||||
var field = List.fields[name];
|
||||
|
||||
if (name) {
|
||||
$td.attr('data-name', name);
|
||||
} else if (string) {
|
||||
$td.attr('data-string', string);
|
||||
}
|
||||
if (!field) {
|
||||
return $td;
|
||||
}
|
||||
var description = string || field.string;
|
||||
|
||||
// search elements
|
||||
var search_div = $('<div>').attr({
|
||||
'class': 'search_div d-inline-flex auto-overflow'
|
||||
});
|
||||
var $input = $('<input>').attr({
|
||||
name: name,
|
||||
data_name: name,
|
||||
data_text: string,
|
||||
placeholder: description,
|
||||
autocomplete: "autocomplete",
|
||||
'class': 'input_text min-wp-50'
|
||||
});
|
||||
var search_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'search_button fa fa-search'
|
||||
});
|
||||
var remove_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'remove_button fa fa-remove',
|
||||
});
|
||||
|
||||
// Prepare search bars
|
||||
if (!widget || widget_list.includes(widget)) {
|
||||
if (string !== " " && field.store) {
|
||||
if (field.type == 'selection') {
|
||||
var selection_options = []
|
||||
for (let option = -1; option < field.selection.length - 1; option += 1) {
|
||||
selection_options.push((field.selection[option], field.selection[option + 1]))
|
||||
}
|
||||
|
||||
const selection_input = '<select class="input_text input_select" type="select" name=' + name + ' data_name=' + name + ' data_text=' + string + ' placeholder=' + description + ' style="height:25px;">'
|
||||
var option = '<option selected="selected" class="text-muted" value> -- 选择 -- </option>';
|
||||
$.each(field.selection, function (index, value) {
|
||||
option += '<option value="' + value[0] + '">' + value[1] + '</option>'
|
||||
$(selection_input).add('<option value="' + value[0] + '">' + value[1] + '</option>')
|
||||
});
|
||||
if (value_of_tr) {
|
||||
if (typeof value_of_tr == 'string') {
|
||||
value_of_tr = [value_of_tr];
|
||||
}
|
||||
value_of_tr.forEach((val) => {
|
||||
var $value_tag = $('<span>').attr({
|
||||
title: val,
|
||||
name: val,
|
||||
value: val,
|
||||
class: 'd-inline-flex badge',
|
||||
})
|
||||
var tag_val = "";
|
||||
field.selection.forEach((value) => {
|
||||
if (val == value[0]) {
|
||||
tag_val = value[1];
|
||||
}
|
||||
});
|
||||
$value_tag.append($(
|
||||
`<div class="o_tag_badge_text">${tag_val}</div>`
|
||||
))
|
||||
$value_tag.val(val);
|
||||
$value_tag.css('background-color', '#dfdfdf');
|
||||
$value_tag.css('border-radius', '20px');
|
||||
$value_tag.css('font-size', '15px');
|
||||
search_div.append($value_tag)
|
||||
})
|
||||
}
|
||||
|
||||
var sel_in = selection_input + option + '</select>'
|
||||
search_div.find('span').append(remove_button);
|
||||
search_div.append(sel_in)
|
||||
search_div.append(search_button)
|
||||
$td.append(search_div)
|
||||
}
|
||||
if (['integer', 'float', 'monetary'].includes(field.type)) {
|
||||
$input.attr({ type: 'number' });
|
||||
|
||||
$input.text(description)
|
||||
if (value_of_tr) {
|
||||
if (typeof value_of_tr == 'number') {
|
||||
value_of_tr = [value_of_tr];
|
||||
}
|
||||
value_of_tr.forEach((val) => {
|
||||
var $value_tag = $('<span>').attr({
|
||||
title: val,
|
||||
name: val,
|
||||
value: val,
|
||||
class: 'd-inline-flex badge',
|
||||
})
|
||||
$value_tag.append($(
|
||||
`<div class="o_tag_badge_text">${val}</div>`
|
||||
))
|
||||
$value_tag.val(val);
|
||||
$value_tag.css('background-color', '#dfdfdf');
|
||||
$value_tag.css('border-radius', '20px');
|
||||
$value_tag.css('font-size', '15px');
|
||||
search_div.append($value_tag)
|
||||
})
|
||||
}
|
||||
search_div.find('span').append(remove_button);
|
||||
search_div.append($input);
|
||||
search_div.append(search_button);
|
||||
$td.append(search_div);
|
||||
}
|
||||
if (['many2one', 'char'].includes(field.type)) {
|
||||
$input.attr({ type: 'text' });
|
||||
|
||||
$input.text(description)
|
||||
const values_of_tr = searched_value.map((val) => {
|
||||
return val[2];
|
||||
})
|
||||
if (values_of_tr.length) {
|
||||
values_of_tr.forEach((val) => {
|
||||
var $value_tag = $('<span>').attr({
|
||||
title: val,
|
||||
name: val,
|
||||
value: val,
|
||||
class: 'd-inline-flex badge',
|
||||
})
|
||||
$value_tag.append($(
|
||||
`<div class="o_tag_badge_text">${val}</div>`
|
||||
))
|
||||
$value_tag.val(val);
|
||||
$value_tag.append(remove_button);
|
||||
$value_tag.css('background-color', '#dfdfdf');
|
||||
$value_tag.css('border-radius', '20px');
|
||||
$value_tag.css('font-size', '15px');
|
||||
search_div.append($value_tag)
|
||||
})
|
||||
}
|
||||
search_div.find('span').append(remove_button);
|
||||
search_div.append($input);
|
||||
search_div.append(search_button);
|
||||
$td.append(search_div);
|
||||
}
|
||||
if (field.type == 'datetime' || field.type == 'date') {
|
||||
var value_of_tr1 = searched_value.length > 1 ? (searched_value[0][2]).split(' ')[0] + '/' + (searched_value[1][2]).split(' ')[0] : ""
|
||||
var search_div = $('<div>').attr({
|
||||
'class': 'search_div'
|
||||
});
|
||||
var $input_date1 = $('<input>').attr({
|
||||
type: 'text',
|
||||
name: name,
|
||||
placeholder: '日期选择',
|
||||
autocomplete: "autocomplete",
|
||||
width: '150px',
|
||||
'class': 'input_text',
|
||||
'style': 'min-width:200px !important'
|
||||
});
|
||||
var search_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'search_button fa fa-search'
|
||||
});
|
||||
var remove_button = $('<button>').attr({
|
||||
type: 'button',
|
||||
name: name,
|
||||
'class': 'remove_button fa fa-remove hidden_button',
|
||||
});
|
||||
$input_date1.daterangepicker({
|
||||
locale: {
|
||||
applyLabel: '应用',
|
||||
cancelLabel: '取消',
|
||||
},
|
||||
});
|
||||
$input_date1.on('apply.daterangepicker', function (ev, picker) {
|
||||
$input_date1.val(picker.startDate.format('YYYY-MM-DD') + '/' + picker.endDate.format('YYYY-MM-DD'));
|
||||
var e = $.Event("keypress", {
|
||||
which: 13
|
||||
});
|
||||
$input_date1.trigger(e);
|
||||
e.keyCode = 13
|
||||
self._onfilter(e)
|
||||
$input_date1.change()
|
||||
});
|
||||
|
||||
$input_date1.on('cancel.daterangepicker', function (ev, picker) {
|
||||
$input_date1.val('');
|
||||
var e = $.Event("keypress", {
|
||||
which: 13
|
||||
});
|
||||
$input_date1.trigger(e);
|
||||
e.keyCode = 13
|
||||
self._onfilter(e)
|
||||
$input_date1.change()
|
||||
});
|
||||
$input_date1.text(description)
|
||||
$input_date1.val(value_of_tr1)
|
||||
|
||||
if (value_of_tr1) {
|
||||
$(search_button).addClass('hidden_button');
|
||||
$(remove_button).removeClass('hidden_button');
|
||||
}
|
||||
if ($(search_button).hasClass('hidden_button')) {
|
||||
$input_date1.attr('readonly', 'readonly');
|
||||
$input_date1.css('background-color', '#dfdfdf');
|
||||
$input_date1.css('border-radius', '20px');
|
||||
}
|
||||
search_div.append($input_date1)
|
||||
search_div.append(remove_button)
|
||||
search_div.append(search_button)
|
||||
$td.append(search_div)
|
||||
}
|
||||
}
|
||||
}
|
||||
return $td;
|
||||
},
|
||||
|
||||
_onKeyPress: function (ev) {
|
||||
ev.stopPropagation(); // to prevent jquery's blockUI to cancel event
|
||||
},
|
||||
|
||||
_onSearch: function (event) {
|
||||
var self = this
|
||||
var $target = $(event.currentTarget);
|
||||
var field_name = $target.attr('name')
|
||||
let all_input_tags = $target.parents('tr').find('input.input_text,select.input_text')
|
||||
if (!all_input_tags.length) {
|
||||
all_input_tags = $($('tr')[5]).find('input.input_text,select.input_text')
|
||||
}
|
||||
all_input_tags.each(function (el) {
|
||||
if (all_input_tags[el].name == field_name) {
|
||||
var e = $.Event("keypress", {
|
||||
which: 13
|
||||
});
|
||||
$(all_input_tags[el]).trigger(e);
|
||||
e.keyCode = 13
|
||||
self._onfilter(e)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_onRemove: function (event) {
|
||||
const List = this.props.list;
|
||||
var self = this;
|
||||
var updated_domain = [];
|
||||
var old_domain = JSON.parse(browser.sessionStorage.getItem('domainlist'));
|
||||
var field_name = $(event.currentTarget).attr('name');
|
||||
var tag_value = $(event.currentTarget.parentElement).attr('value');
|
||||
let all_input_tags = $($('tr')[1]).find('input.input_text');
|
||||
|
||||
if (!all_input_tags.length) {
|
||||
all_input_tags = $($('tr')[5]).find('input.input_text')
|
||||
}
|
||||
const ListDomain = old_domain;
|
||||
if (["monetary", "float", "integer", "selection"].includes(List.fields[field_name].type)) {
|
||||
ListDomain.map(function (el) {
|
||||
tag_value = !isNaN(tag_value) ? parseFloat(tag_value) : tag_value;
|
||||
if (field_name == el[0] && el[2].includes(tag_value)) {
|
||||
const index = el[2].indexOf(tag_value);
|
||||
index > -1 ? el[2].splice(index, 1) : false;
|
||||
el[2].length ? updated_domain.push(el) : false;
|
||||
}
|
||||
else {
|
||||
updated_domain.push(el)
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (["date", "datetime"].includes(List.fields[field_name].type)) {
|
||||
ListDomain.map(function (el) {
|
||||
if (field_name != el[0]) {
|
||||
updated_domain.push(el)
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
ListDomain.map(function (el) {
|
||||
if (field_name != el[0] || tag_value != el[2]) {
|
||||
updated_domain.push(el)
|
||||
}
|
||||
if (el == '|') {
|
||||
let index = updated_domain.indexOf('|');
|
||||
updated_domain.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify(updated_domain));
|
||||
var indexes = []
|
||||
var to_update = []
|
||||
var count = 0
|
||||
updated_domain.forEach(el => {
|
||||
if (indexes.length && el[1] == 'ilike' && updated_domain[indexes[indexes.length - 1]][0] == el[0]) {
|
||||
to_update.push(indexes[indexes.length - 1])
|
||||
} else if (el[1] == 'ilike') {
|
||||
indexes.push(count)
|
||||
}
|
||||
count += 1;
|
||||
})
|
||||
to_update.reverse()
|
||||
to_update.forEach(A => {
|
||||
updated_domain.splice(A, 0, '|');
|
||||
})
|
||||
self.perform_search(self, updated_domain);
|
||||
self._renderSearchPanel();
|
||||
},
|
||||
|
||||
_onfilter: function (e) {
|
||||
const List = this.props.list;
|
||||
const old_domain = JSON.parse(browser.sessionStorage.getItem('domainlist'));
|
||||
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
var domain_list = [];
|
||||
var self = this
|
||||
var $target = $(e.target);
|
||||
let all_input_tags = $target.parents('tr').find('input.input_text')
|
||||
if (!all_input_tags.length) {
|
||||
all_input_tags = $($('tr')[5]).find('input.input_text')
|
||||
}
|
||||
|
||||
let all_select_tags = $target.parents('tr').find('select.input_text')
|
||||
if (!all_select_tags.length) {
|
||||
all_select_tags = $($('tr')[5]).find('select.input_text')
|
||||
}
|
||||
|
||||
var search_tr = all_input_tags.filter(function (el) {
|
||||
return all_input_tags[el].value
|
||||
});
|
||||
var search_select = all_select_tags.filter(function (el) {
|
||||
return all_select_tags[el].value
|
||||
});
|
||||
|
||||
/* Checking if both the variables `search_tr` and `search_select` are
|
||||
empty. If both variables are empty, it will return without executing the further code. */
|
||||
if (!search_tr.length && !search_select.length) {
|
||||
// self.perform_search(self, domain_list);
|
||||
return
|
||||
}
|
||||
|
||||
for (var i = 0; i < search_tr.length; i++) {
|
||||
var field = $(search_tr[i]).attr('name')
|
||||
var string = search_tr[i].textContent
|
||||
var value = $(search_tr[i]).val()
|
||||
if (value) {
|
||||
if (List.fields[field].type == "date") {
|
||||
var startDate = moment(value.split('/')[0]).format('YYYY-MM-DD');
|
||||
var endDate = moment(value.split('/')[1]).format('YYYY-MM-DD');
|
||||
if (startDate == 'Invalid date' || endDate == 'Invalid date') {
|
||||
window.alert("Please enter valid input for " + string);
|
||||
} else {
|
||||
domain_list.push([field, '>=', startDate])
|
||||
domain_list.push([field, '<=', endDate])
|
||||
}
|
||||
} else if (List.fields[field].type == "datetime") {
|
||||
var startDate = moment(value.split('/')[0]).format('YYYY-MM-DD 00:00:00');
|
||||
var endDate = moment(value.split('/')[1]).format('YYYY-MM-DD 23:59:59');
|
||||
if (startDate == 'Invalid date' || endDate == 'Invalid date') {
|
||||
window.alert("Please enter valid input for " + string);
|
||||
} else {
|
||||
domain_list.push([field, '>=', startDate])
|
||||
domain_list.push([field, '<=', endDate])
|
||||
}
|
||||
|
||||
} else if (["monetary", "float", "integer"].includes(List.fields[field].type)) {
|
||||
var domain = old_domain.filter((domain) => {
|
||||
return domain[0] == field;
|
||||
})
|
||||
if (domain.length) {
|
||||
if (!domain[0][2].includes(parseFloat(value))) {
|
||||
domain[0][2].push(parseFloat(value));
|
||||
}
|
||||
domain_list.push(domain[0])
|
||||
}
|
||||
else {
|
||||
domain_list.push([field, 'in', [parseFloat(value)]]);
|
||||
}
|
||||
} else {
|
||||
if (old_domain.length) {
|
||||
old_domain.forEach((domain) => {
|
||||
if (domain[0] == field) {
|
||||
domain_list.push(domain);
|
||||
}
|
||||
})
|
||||
if (!(old_domain.map((d) => { return d[2] }).includes(value) &&
|
||||
old_domain.map((d) => { return d[0] }).includes(field))) {
|
||||
domain_list.push([field, 'ilike', value]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
domain_list.push([field, 'ilike', value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$(all_select_tags).each(function (index, elem) {
|
||||
var elem_value = $(elem).val();
|
||||
var elem_field = $(elem).attr('data_name');
|
||||
if (elem_value.length > 0) {
|
||||
if (List.fields[elem_field].type == 'selection') {
|
||||
var domain = old_domain.filter((domain) => {
|
||||
return domain[0] == elem_field
|
||||
})
|
||||
if (domain.length) {
|
||||
if (!domain[0][2].includes(elem_value)) {
|
||||
domain[0][2].push(elem_value);
|
||||
}
|
||||
domain_list.push(domain[0])
|
||||
}
|
||||
else {
|
||||
domain_list.push([elem_field, 'in', [elem_value]])
|
||||
}
|
||||
}
|
||||
else {
|
||||
domain_list.push([elem_field, '=', elem_value])
|
||||
}
|
||||
}
|
||||
});
|
||||
old_domain.forEach((domain) => {
|
||||
var avail = domain_list.filter(a => {
|
||||
if (domain[1] == 'in') {
|
||||
return domain[2].every(f => a[2].includes(f)) && a[0] == domain[0]
|
||||
}
|
||||
else {
|
||||
return a[2].toString() == domain[2].toString() && a[0] == domain[0]
|
||||
}
|
||||
|
||||
}).length
|
||||
if (!(avail)) {
|
||||
domain_list.push(domain)
|
||||
}
|
||||
})
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify(domain_list));
|
||||
var indexes = []
|
||||
var to_update = []
|
||||
var count = 0
|
||||
domain_list.forEach(el => {
|
||||
if (indexes.length && el[1] == 'ilike' && domain_list[indexes[indexes.length - 1]][0] == el[0]) {
|
||||
to_update.push(indexes[indexes.length - 1])
|
||||
} else if (el[1] == 'ilike') {
|
||||
indexes.push(count)
|
||||
}
|
||||
count += 1;
|
||||
})
|
||||
to_update.reverse()
|
||||
to_update.forEach(A => {
|
||||
domain_list.splice(A, 0, '|');
|
||||
})
|
||||
self.perform_search(self, domain_list);
|
||||
}
|
||||
},
|
||||
|
||||
perform_search: async function (self, domain_list) {
|
||||
const searchModel = self.env.searchModel;
|
||||
const list = self.props.list;
|
||||
|
||||
|
||||
if (self.isX2Many) {
|
||||
const bm_params = list.model.config;
|
||||
/**
|
||||
* Function will not apply to new records.
|
||||
*/
|
||||
if (!(bm_params.resId || bm_params.resIds.length)) {
|
||||
browser.sessionStorage.setItem('domainlist', JSON.stringify([]));
|
||||
return;
|
||||
}
|
||||
var res_id = bm_params.resId ? bm_params.resId : bm_params.resIds[0]
|
||||
const readIds = await self.env.model.orm.read(
|
||||
self.props.nestedKeyOptionalFieldsData.model,
|
||||
[res_id],
|
||||
[self.props.nestedKeyOptionalFieldsData.field])
|
||||
|
||||
domain_list.push(['id', 'in', readIds[0][self.props.nestedKeyOptionalFieldsData.field]])
|
||||
|
||||
var resIds = await self.env.model.orm.search(list.resModel, domain_list, {
|
||||
limit: session.active_ids_limit,
|
||||
context: list.context,
|
||||
});
|
||||
await list._load({'nextCurrentIds': resIds})
|
||||
}
|
||||
else {
|
||||
searchModel.ak_domains = domain_list;
|
||||
searchModel._notify();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
patch(ListRenderer.prototype, ListRendererCust);
|
||||
@@ -0,0 +1,52 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {ListRenderer} from "@web/views/list/list_renderer";
|
||||
|
||||
patch(ListRenderer.prototype, {
|
||||
setup(){
|
||||
super.setup();
|
||||
this.numSwitch = this.constructor.name === 'ListRenderer';
|
||||
},
|
||||
|
||||
// The following code manipulates the DOM directly to avoid having to wait for a
|
||||
// render + patch which would occur on the next frame and cause flickering.
|
||||
freezeColumnWidths() {
|
||||
if (!this.keepColumnWidths) {
|
||||
this.columnWidths = null;
|
||||
}
|
||||
|
||||
const table = this.tableRef.el;
|
||||
const headers = [...table.querySelectorAll("thead th:not(.o_list_actions_header)")];
|
||||
|
||||
if (!this.columnWidths || !this.columnWidths.length) {
|
||||
// no column widths to restore
|
||||
|
||||
table.style.tableLayout = "fixed";
|
||||
const allowedWidth = table.parentNode.getBoundingClientRect().width;
|
||||
// Set table layout auto and remove inline style to make sure that css
|
||||
// rules apply (e.g. fixed width of record selector)
|
||||
table.style.tableLayout = "auto";
|
||||
headers.forEach((th) => {
|
||||
th.style.width = null;
|
||||
th.style.maxWidth = null;
|
||||
});
|
||||
|
||||
this.setDefaultColumnWidths();
|
||||
|
||||
// Squeeze the table by applying a max-width on largest columns to
|
||||
// ensure that it doesn't overflow
|
||||
this.columnWidths = this.computeColumnWidthsFromContent(allowedWidth);
|
||||
table.style.tableLayout = "fixed";
|
||||
}
|
||||
headers.forEach((th, index) => {
|
||||
if (!th.style.width) {
|
||||
th.style.width = `${Math.floor(this.columnWidths[index])}px`;
|
||||
}
|
||||
if (Array.from(th.classList).includes('list-number')){
|
||||
th.style.width = `${String(this.props.list.offset || this.props.list.limit).length * 20}px`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-inherit="web.ListRenderer" t-inherit-mode="extension">
|
||||
<xpath expr="//thead//th[@t-if='hasSelectors']" position="before">
|
||||
<t t-if="numSwitch">
|
||||
<th class="o_list_record_selector list-number">#</th>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//tfoot//td[@t-if='hasSelectors']" position="before">
|
||||
<t t-if="numSwitch">
|
||||
<td class="o_list_record_selector"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-inherit="web.ListRenderer.RecordRow" t-inherit-mode="extension">
|
||||
<xpath expr="//td[@t-if='hasSelectors']" position="before">
|
||||
<t t-if="numSwitch">
|
||||
<t t-set="num" t-value="(record_index + 1) + list.offset"/>
|
||||
<td t-esc="num" t-att-data-tooltip="num" class="cursor-pointer"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-inherit="web.ListRenderer.Rows" t-inherit-mode="extension">
|
||||
<xpath expr="//tr[@t-foreach='getEmptyRowIds']/td" position="before">
|
||||
<t t-if="numSwitch">
|
||||
<td/>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//td[@t-if='withHandleColumn']" position="before">
|
||||
<t t-if="numSwitch">
|
||||
<td/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-inherit="web.ListRenderer.GroupRow" t-inherit-mode="extension">
|
||||
<xpath expr="//th[@tabindex='-1']" position="before">
|
||||
<t t-if="numSwitch">
|
||||
<t t-set="num" t-value="(group_index + 1) + list.offset"/>
|
||||
<td t-esc="num" t-att-data-tooltip="num" class="cursor-pointer"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||