Browse Source

May 30 [UPDT] : Bug Fixed 'product_visibility_website'

pull/254/head
AjmalCybro 2 years ago
parent
commit
d560941dcf
  1. 2
      product_visibility_website/__manifest__.py
  2. 122
      product_visibility_website/controllers/main.py
  3. 2
      product_visibility_website/models/__init__.py
  4. 55
      product_visibility_website/models/product_public_category.py
  5. 58
      product_visibility_website/models/product_template.py
  6. 12
      product_visibility_website/models/website_product_visibility.py
  7. 294
      product_visibility_website/static/src/js/000.js

2
product_visibility_website/__manifest__.py

@ -24,7 +24,7 @@
{ {
'name': 'Website Product Visibility', 'name': 'Website Product Visibility',
'summary': 'Website Product visibility for Users', 'summary': 'Website Product visibility for Users',
'version': '15.0.1.0.1', 'version': '15.0.1.0.2',
'description': """Website Product visibility for Users""", 'description': """Website Product visibility for Users""",
'author': 'Cybrosys Techno Solution', 'author': 'Cybrosys Techno Solution',
'maintainer': 'Cybrosys Techno Solutions', 'maintainer': 'Cybrosys Techno Solutions',

122
product_visibility_website/controllers/main.py

@ -73,22 +73,20 @@ class ProductVisibilityCon(WebsiteSale):
if categories: if categories:
domains.append([('public_categ_ids', 'child_of', categories.ids)]) domains.append([('public_categ_ids', 'child_of', categories.ids)])
if attrib_values: if attrib_values:
print("hello world....") attrib = None
# attrib = None ids = []
# ids = [] for value in attrib_values:
# print("hello...", ids) if not attrib:
# for value in attrib_values: attrib = value[0]
# if not attrib: ids.append(value[1])
# attrib = value[0] elif value[0] == attrib:
# ids.append(value[1]) ids.append(value[1])
# elif value[0] == attrib: else:
# ids.append(value[1]) domains.append([('attribute_line_ids.value_ids', 'in', ids)])
# else: attrib = value[0]
# domains.append([('attribute_line_ids.value_ids', 'in', ids)]) ids = [value[1]]
# attrib = value[0] if attrib:
# ids = [value[1]] domains.append([('attribute_line_ids.value_ids', 'in', ids)])
# if attrib:
# domains.append([('attribute_line_ids.value_ids', 'in', ids)])
return expression.AND(domains) return expression.AND(domains)
@ -231,6 +229,7 @@ class ProductVisibilityCon(WebsiteSale):
if category: if category:
values['main_object'] = category values['main_object'] = category
return request.render("website_sale.products", values) return request.render("website_sale.products", values)
def availavle_products(self): def availavle_products(self):
@ -239,94 +238,3 @@ class ProductVisibilityCon(WebsiteSale):
partner = request.env['res.partner'].sudo().search([('id', '=', user.partner_id.id)]) partner = request.env['res.partner'].sudo().search([('id', '=', user.partner_id.id)])
return partner.website_available_product_ids return partner.website_available_product_ids
# --------------------------------------------------------------------------
# Products Search Bar
# --------------------------------------------------------------------------
@http.route('/shop/products/autocomplete', type='json', auth='public', website=True)
def products_autocomplete(self, term, options={}, **kwargs):
"""
Returns list of products according to the term and product options
Params:
term (str): search term written by the user
options (dict)
- 'limit' (int), default to 5: number of products to consider
- 'display_description' (bool), default to True
- 'display_price' (bool), default to True
- 'order' (str)
- 'max_nb_chars' (int): max number of characters for the
description if returned
Returns:
dict (or False if no result)
- 'products' (list): products (only their needed field values)
note: the prices will be strings properly formatted and
already containing the currency
- 'products_count' (int): the number of products in the database
that matched the search query
"""
user = request.env['res.users'].sudo().search([('id', '=', request.env.user.id)])
available_categ = available_products = ''
if not user:
mode = request.env['ir.config_parameter'].sudo().get_param('filter_mode')
products = literal_eval(
request.env['ir.config_parameter'].sudo().get_param('website_product_visibility.available_product_ids',
'False'))
if mode == 'product_only':
available_products = request.env['product.template'].search([('id', 'in', products)])
cat = literal_eval(
request.env['ir.config_parameter'].sudo().get_param('website_product_visibility.available_cat_ids',
'False'))
available_categ = request.env['product.public.category'].search([('id', 'in', cat)])
else:
partner = request.env['res.partner'].sudo().search([('id', '=', user.partner_id.id)])
mode = partner.filter_mode
if mode != 'categ_only':
available_products = self.availavle_products()
available_categ = partner.website_available_cat_ids
ProductTemplate = request.env['product.template']
display_description = options.get('display_description', True)
display_price = options.get('display_price', True)
order = self._get_search_order(options)
max_nb_chars = options.get('max_nb_chars', 999)
category = options.get('category')
attrib_values = options.get('attrib_values')
if not available_products and not available_categ:
domain = self._get_search_domain(term, category, attrib_values, display_description)
else:
domain = self.reset_domain(term,available_categ, available_products, attrib_values,display_description)
products = ProductTemplate.search(
domain,
limit=min(20, options.get('limit', 5)),
order=order
)
fields = ['id', 'name', 'website_url']
if display_description:
fields.append('description_sale')
res = {
'products': products.read(fields),
'products_count': ProductTemplate.search_count(domain),
}
if display_description:
for res_product in res['products']:
desc = res_product['description_sale']
if desc and len(desc) > max_nb_chars:
res_product['description_sale'] = "%s..." % desc[:(max_nb_chars - 3)]
if display_price:
FieldMonetary = request.env['ir.qweb.field.monetary']
monetary_options = {
'display_currency': request.website.get_current_pricelist().currency_id,
}
for res_product, product in zip(res['products'], products):
combination_info = product._get_combination_info(only_template=True)
res_product.update(combination_info)
res_product['list_price'] = FieldMonetary.value_to_html(res_product['list_price'], monetary_options)
res_product['price'] = FieldMonetary.value_to_html(res_product['price'], monetary_options)
return res

2
product_visibility_website/models/__init__.py

@ -22,3 +22,5 @@
################################################################################### ###################################################################################
from . import website_product_visibility from . import website_product_visibility
from . import product_public_category
from . import product_template

55
product_visibility_website/models/product_public_category.py

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies (<https://www.cybrosys.com>).
# Author: Neeraj Krishnan V M(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
###################################################################################
from ast import literal_eval
from odoo import api, models
class ProductPublicCategory(models.Model):
_inherit = 'product.public.category'
"""Inherit the product.public.category model to filter the categories based
on the user's filter mode"""
@api.model
def _search_fetch(self, search_detail, search, limit, order):
results, count = super(ProductPublicCategory, self)._search_fetch\
(search_detail, search, limit, order)
filter_mode = self.env['ir.config_parameter'].sudo().get_param\
('filter_mode')
if not self.env.user.active and filter_mode == 'categ_only':
category = literal_eval(self.env['ir.config_parameter'].sudo().get_param(
'website_product_visibility.available_cat_ids'))
results = results.filtered(lambda r: r.id in category)
else:
partner = self.env.user.partner_id
if partner.filter_mode == 'categ_only':
category = partner.website_available_cat_ids.ids
results = results.filtered(lambda r: r.id in category)
elif partner.filter_mode == 'product_only':
products = partner.website_available_product_ids.ids
results = results.filtered(lambda r: any(item in r.product_tmpl_ids.ids
for item in products))
return results, len(results)

58
product_visibility_website/models/product_template.py

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies (<https://www.cybrosys.com>).
# Author: Neeraj Krishnan V M(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
###################################################################################
from ast import literal_eval
from odoo import api, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
"""Inherit Product Template to add filter mode for website users"""
@api.model
def _search_fetch(self, search_detail, search, limit, order):
results, count = super(ProductTemplate, self)._search_fetch(
search_detail, search, limit, order)
filter_mode = self.env['ir.config_parameter'].sudo().get_param(
'filter_mode')
if not self.env.user.active:
if filter_mode == 'categ_only':
category = literal_eval(
self.env['ir.config_parameter'].sudo().get_param(
'website_product_visibility.available_cat_ids'))
results = results.filtered(lambda r: any(
item in r.public_categ_ids.ids for item in category))
elif filter_mode == 'product_only':
products = literal_eval(
self.env['ir.config_parameter'].sudo().get_param(
'website_product_visibility.available_product_ids'))
results = results.filtered(lambda r: r.id in products)
else:
partner = self.env.user.partner_id
if partner.filter_mode == 'categ_only':
category = partner.website_available_cat_ids.ids
results = results.filtered(lambda r: any(
item in r.public_categ_ids.ids for item in category))
elif partner.filter_mode == 'product_only':
products = partner.website_available_product_ids.ids
results = results.filtered(lambda r: r.id in products)
return results, len(results)

12
product_visibility_website/models/website_product_visibility.py

@ -51,9 +51,11 @@ class WebsiteGuestVisibility(models.TransientModel):
product_visibility_guest_user = fields.Boolean(string="Product visibility Guest User") product_visibility_guest_user = fields.Boolean(string="Product visibility Guest User")
filter_mode = fields.Selection([('product_only', 'Product Wise'), filter_mode = fields.Selection([('product_only', 'Product Wise'),
('categ_only', 'Category Wise')], string='Filter Mode', default='product_only') ('categ_only', 'Category Wise')],
string='Filter Mode', default='product_only')
available_product_ids = fields.Many2many('product.template', string='Available Product', available_product_ids = fields.Many2many('product.template',
string='Available Product',
domain="[('is_published', '=', True)]", domain="[('is_published', '=', True)]",
help='The website will only display products which are within one ' help='The website will only display products which are within one '
'of the selected category trees. If no category is specified,' 'of the selected category trees. If no category is specified,'
@ -87,8 +89,10 @@ class WebsiteGuestVisibility(models.TransientModel):
@api.model @api.model
def get_values(self): def get_values(self):
res = super(WebsiteGuestVisibility, self).get_values() res = super(WebsiteGuestVisibility, self).get_values()
product_ids = literal_eval(self.env['ir.config_parameter'].sudo().get_param('website_product_visibility.available_product_ids', 'False')) product_ids = literal_eval(self.env['ir.config_parameter'].sudo()
cat_ids = literal_eval(self.env['ir.config_parameter'].sudo().get_param('website_product_visibility.available_cat_ids', 'False')) .get_param('website_product_visibility.available_product_ids', 'False'))
cat_ids = literal_eval(self.env['ir.config_parameter'].sudo()
.get_param('website_product_visibility.available_cat_ids', 'False'))
mod = self.env['ir.config_parameter'].sudo().get_param('filter_mode') mod = self.env['ir.config_parameter'].sudo().get_param('filter_mode')
if self.env['ir.config_parameter'].sudo().get_param('filter_mode'): if self.env['ir.config_parameter'].sudo().get_param('filter_mode'):

294
product_visibility_website/static/src/js/000.js

@ -0,0 +1,294 @@
/** @odoo-module **/
import concurrency from 'web.concurrency';
import publicWidget from 'web.public.widget';
import {qweb} from 'web.core';
import {Markup} from 'web.utils';
publicWidget.registry.searchBar = publicWidget.Widget.extend({
selector: '.o_searchbar_form',
xmlDependencies: ['/website/static/src/snippets/s_searchbar/000.xml'],
events: {
'input .search-query': '_onInput',
'focusout': '_onFocusOut',
'keydown .search-query': '_onKeydown',
'search .search-query': '_onSearch',
},
autocompleteMinWidth: 300,
/**
* @constructor
*/
init: function () {
this._super.apply(this, arguments);
this._dp = new concurrency.DropPrevious();
this._onInput = _.debounce(this._onInput, 400);
this._onFocusOut = _.debounce(this._onFocusOut, 100);
},
/**
* @override
*/
start: function () {
this.$input = this.$('.search-query');
this.searchType = this.$input.data('searchType');
this.order = this.$('.o_search_order_by').val();
this.limit = parseInt(this.$input.data('limit'));
this.displayDescription = this.$input.data('displayDescription');
this.displayExtraLink = this.$input.data('displayExtraLink');
this.displayDetail = this.$input.data('displayDetail');
this.displayImage = this.$input.data('displayImage');
this.wasEmpty = !this.$input.val();
// Make it easy for customization to disable fuzzy matching on specific searchboxes
this.allowFuzzy = !this.$input.data('noFuzzy');
if (this.limit) {
this.$input.attr('autocomplete', 'off');
}
this.options = {
'displayImage': this.displayImage,
'displayDescription': this.displayDescription,
'displayExtraLink': this.displayExtraLink,
'displayDetail': this.displayDetail,
'allowFuzzy': this.allowFuzzy,
};
const form = this.$('.o_search_order_by').parents('form');
for (const field of form.find("input[type='hidden']")) {
this.options[field.name] = field.value;
}
const action = form.attr('action') || window.location.pathname + window.location.search;
const [urlPath, urlParams] = action.split('?');
if (urlParams) {
for (const keyValue of urlParams.split('&')) {
const [key, value] = keyValue.split('=');
if (value && key !== 'search') {
this.options[key] = value;
}
}
}
const pathParts = urlPath.split('/');
for (const index in pathParts) {
const value = pathParts[index];
if (index > 0 && /-[0-9]+$/.test(value)) { // is sluggish
this.options[pathParts[index - 1]] = value;
}
}
if (this.$input.data('noFuzzy')) {
$("<input type='hidden' name='noFuzzy' value='true'/>").appendTo(this.$input);
}
return this._super.apply(this, arguments);
},
/**
* @override
*/
destroy() {
this._super(...arguments);
this._render(null);
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
*/
_adaptToScrollingParent() {
const bcr = this.el.getBoundingClientRect();
this.$menu[0].style.setProperty('position', 'fixed', 'important');
this.$menu[0].style.setProperty('top', `${bcr.bottom}px`, 'important');
this.$menu[0].style.setProperty('left', `${bcr.left}px`, 'important');
this.$menu[0].style.setProperty('max-width', `${bcr.width}px`, 'important');
this.$menu[0].style.setProperty('max-height', `${document.body.clientHeight - bcr.bottom - 16}px`, 'important');
},
/**
* @private
*/
async _fetch() {
const res = await this._rpc({
route: '/website/snippet/autocomplete',
params: {
'search_type': this.searchType,
'term': this.$input.val(),
'order': this.order,
'limit': this.limit,
'max_nb_chars': Math.round(Math.max(this.autocompleteMinWidth, parseInt(this.$el.width())) * 0.22),
'options': this.options,
},
});
const fieldNames = [
'name',
'description',
'extra_link',
'detail',
'detail_strike',
'detail_extra',
];
res.results.forEach(record => {
for (const fieldName of fieldNames) {
if (record[fieldName]) {
if (typeof record[fieldName] === "object") {
for (const fieldKey of Object.keys(record[fieldName])) {
record[fieldName][fieldKey] = Markup(record[fieldName][fieldKey]);
}
} else {
record[fieldName] = Markup(record[fieldName]);
}
}
}
});
return res;
},
/**
* @private
*/
_render: function (res) {
console.trace(res.results)
if (this._scrollingParentEl) {
this._scrollingParentEl.removeEventListener('scroll', this._menuScrollAndResizeHandler);
window.removeEventListener('resize', this._menuScrollAndResizeHandler);
delete this._scrollingParentEl;
delete this._menuScrollAndResizeHandler;
}
const $prevMenu = this.$menu;
this.$el.toggleClass('dropdown show', !!res);
if (res && this.limit) {
const results = res['results'];
let template = 'website.s_searchbar.autocomplete';
const candidate = template + '.' + this.searchType;
if (qweb.has_template(candidate)) {
template = candidate;
}
console.log('vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv',template)
this.$menu = $(qweb.render(template, {
results: results,
parts: res['parts'],
hasMoreResults: results.length < res['results_count'],
search: this.$input.val(),
fuzzySearch: res['fuzzy_search'],
widget: this,
}));
// TODO adapt directly in the template in master
const mutedItemTextEl = this.$menu.find('span.dropdown-item-text.text-muted')[0];
if (mutedItemTextEl) {
const newItemTextEl = document.createElement('span');
newItemTextEl.classList.add('dropdown-item-text');
mutedItemTextEl.after(newItemTextEl);
mutedItemTextEl.classList.remove('dropdown-item-text');
newItemTextEl.appendChild(mutedItemTextEl);
}
this.$menu.css('min-width', this.autocompleteMinWidth);
// Handle the case where the searchbar is in a mega menu by making
// it position:fixed and forcing its size. Note: this could be the
// default behavior or at least needed in more cases than the mega
// menu only (all scrolling parents). But as a stable fix, it was
// easier to fix that case only as a first step, especially since
// this cannot generically work on all scrolling parent.
const megaMenuEl = this.el.closest('.o_mega_menu');
if (megaMenuEl) {
const navbarEl = this.el.closest('.navbar');
const navbarTogglerEl = navbarEl ? navbarEl.querySelector('.navbar-toggler') : null;
if (navbarTogglerEl && navbarTogglerEl.clientWidth < 1) {
this._scrollingParentEl = megaMenuEl;
this._menuScrollAndResizeHandler = () => this._adaptToScrollingParent();
this._scrollingParentEl.addEventListener('scroll', this._menuScrollAndResizeHandler);
window.addEventListener('resize', this._menuScrollAndResizeHandler);
this._adaptToScrollingParent();
}
}
this.$el.append(this.$menu);
this.$el.find('button.extra_link').on('click', function (event) {
event.preventDefault();
window.location.href = event.currentTarget.dataset['target'];
});
this.$el.find('.s_searchbar_fuzzy_submit').on('click', (event) => {
event.preventDefault();
this.$input.val(res['fuzzy_search']);
const form = this.$('.o_search_order_by').parents('form');
form.submit();
});
}
if ($prevMenu) {
$prevMenu.remove();
}
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
*/
_onInput: function () {
if (!this.limit) {
return;
}
if (this.searchType === 'all' && !this.$input.val().trim().length) {
this._render();
} else {
this._dp.add(this._fetch()).then(this._render.bind(this));
}
},
/**
* @private
*/
_onFocusOut: function () {
if (!this.$el.has(document.activeElement).length) {
this._render();
}
},
/**
* @private
*/
_onKeydown: function (ev) {
switch (ev.which) {
case $.ui.keyCode.ESCAPE:
this._render();
break;
case $.ui.keyCode.UP:
case $.ui.keyCode.DOWN:
ev.preventDefault();
if (this.$menu) {
let $element = ev.which === $.ui.keyCode.UP ? this.$menu.children().last() : this.$menu.children().first();
$element.focus();
}
break;
case $.ui.keyCode.ENTER:
this.limit = 0; // prevent autocomplete
break;
}
},
/**
* @private
*/
_onSearch: function (ev) {
if (this.$input[0].value) { // actual search
this.limit = 0; // prevent autocomplete
} else { // clear button clicked
this._render(); // remove existing suggestions
ev.preventDefault();
if (!this.wasEmpty) {
this.limit = 0; // prevent autocomplete
const form = this.$('.o_search_order_by').parents('form');
form.submit();
}
}
},
});
export default {
searchBar: publicWidget.registry.searchBar,
};
Loading…
Cancel
Save