Browse Source

Merge f3646fd969 into 45a15ac568

pull/411/merge
Wolfgang Pichler 4 years ago
committed by GitHub
parent
commit
43641bbc51
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 91
      pos_product_template/README.rst
  2. 20
      pos_product_template/__init__.py
  3. 27
      pos_product_template/__manifest__.py
  4. 31
      pos_product_template/demo/product_attribute_value.xml
  5. 45
      pos_product_template/demo/product_product.xml
  6. 44
      pos_product_template/i18n/es.po
  7. 44
      pos_product_template/i18n/fr.po
  8. 42
      pos_product_template/i18n/pos_product_template.pot
  9. 6
      pos_product_template/readme/CONTRIBUTORS.rst
  10. 3
      pos_product_template/readme/CREDITS.rst
  11. 11
      pos_product_template/readme/DESCRIPTION.rst
  12. 1
      pos_product_template/readme/ROADMAP.rst
  13. 8
      pos_product_template/readme/USAGE.rst
  14. BIN
      pos_product_template/static/description/icon.png
  15. 202
      pos_product_template/static/src/css/ppt.css
  16. BIN
      pos_product_template/static/src/img/screenshots/pos_product_template.png
  17. 575
      pos_product_template/static/src/js/ppt.js
  18. 119
      pos_product_template/static/src/xml/ppt.xml
  19. 14
      pos_product_template/view/view.xml

91
pos_product_template/README.rst

@ -0,0 +1,91 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: https://www.gnu.org/licenses/agpl
:alt: License: AGPL-3
====================
POS Product Template
====================
* In Point Of Sale Front End - Products list:
* Display only one product per template;
* Display template name instead of product name;
* Display products quantity instead of price;
* Click on template displays an extra screen to select Variant;
* In Point Of Sale Front End - Variants list:
* Display all the products of the selected variant;
* Click on a attribute value filters products;
* Click on a product adds it to the current Order or display normal
extra screen if it is a weightable product;
Usage
=====
Open the Point of Sale, search an article with variants.
You will see one article instead of all the variants.
#. Go to ...
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/pos/11.0
Known issues / Roadmap
======================
* Templates with lot of variants are not shown. See https://github.com/OCA/pos/pull/135
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/pos/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://odoo-community.org/logo.png>`_.
Contributors
------------
* Sylvain LE GAL (https://twitter.com/legalsylvain)
* Navarromiguel (https://github.com/navarromiguel)
* Damendieta (https://github.com/damendieta)
* Raphaël Reverdy (https://akretion.com)
* Hugo Santos (https://factorlibre.com)
Do not contact contributors directly about support or help with technical issues.
Funders
-------
The development of this module has been financially supported by:
* Akretion
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

20
pos_product_template/__init__.py

@ -0,0 +1,20 @@
##############################################################################
#
# Point Of Sale - Product Template module for Odoo
# Copyright (C) 2014-Today Akretion (http://www.akretion.com)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License 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 <http://www.gnu.org/licenses/>.
#
##############################################################################

27
pos_product_template/__manifest__.py

@ -0,0 +1,27 @@
{
'name': 'POS - Product Template',
'version': '12.0.1.0.0',
'category': 'Point Of Sale',
'author': "Akretion,Odoo Community Association (OCA)",
'summary': 'Manage Product Template in Front End Point Of Sale',
'website': 'http://www.akretion.com',
'license': 'AGPL-3',
'depends': [
'point_of_sale',
'pos_fix_search_limit',
],
'data': [
'view/view.xml',
],
'qweb': [
'static/src/xml/ppt.xml',
],
'demo': [
'demo/product_attribute_value.xml',
'demo/product_product.xml',
],
'images': [
'static/src/img/screenshots/pos_product_template.png',
],
'installable': True,
}

31
pos_product_template/demo/product_attribute_value.xml

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
##############################################################################
#
# Point Of Sale - Product Template module for Odoo
# Copyright (C) 2014-Today Akretion (http://www.akretion.com)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
-->
<odoo>
<record id="attribute_wifi_extra" model="product.attribute.value">
<field name="attribute_id" eval="ref('product.product_attribute_3')"/>
<field name="name">2.399GHz</field>
</record>
</odoo>

45
pos_product_template/demo/product_product.xml

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
##############################################################################
#
# Point Of Sale - Product Template module for Odoo
# Copyright (C) 2014-Today Akretion (http://www.akretion.com)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
-->
<odoo>
<record id="product.product_product_4" model="product.product">
<field name="available_in_pos" eval="True"/>
</record>
<record id="product.product_product_4b" model="product.product">
<field name="available_in_pos" eval="True"/>
</record>
<record id="product.product_product_4c" model="product.product">
<field name="available_in_pos" eval="True"/>
</record>
<record id="product.product_product_11" model="product.product">
<field name="available_in_pos" eval="True"/>
</record>
<record id="product.product_product_11b" model="product.product">
<field name="available_in_pos" eval="True"/>
</record>
</odoo>

44
pos_product_template/i18n/es.po

@ -0,0 +1,44 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_product_template
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-12-18 12:01+0000\n"
"PO-Revision-Date: 2014-12-18 12:01+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_product_template
#: model:product.attribute.value,name:pos_product_template.attribute_wifi_extra
msgid "2.399GHz"
msgstr ""
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:13
#, python-format
msgid "Cancel"
msgstr "Cancelar"
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:11
#, python-format
msgid "Variant Selection of"
msgstr "Seleccina una variante de"
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:39
#: code:addons/pos_product_template/static/src/xml/ppt.xml:77
#, python-format
msgid "Variants"
msgstr "Variantes"

44
pos_product_template/i18n/fr.po

@ -0,0 +1,44 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_product_template
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-12-18 11:57+0000\n"
"PO-Revision-Date: 2014-12-18 11:57+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_product_template
#: model:product.attribute.value,name:pos_product_template.attribute_wifi_extra
msgid "2.399GHz"
msgstr ""
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:13
#, python-format
msgid "Cancel"
msgstr "Annuler"
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:11
#, python-format
msgid "Variant Selection of"
msgstr "Sélection d'une variante de"
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:39
#: code:addons/pos_product_template/static/src/xml/ppt.xml:77
#, python-format
msgid "Variants"
msgstr "Variantes"

42
pos_product_template/i18n/pos_product_template.pot

@ -0,0 +1,42 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_product_template
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_product_template
#: model:product.attribute.value,name:pos_product_template.attribute_wifi_extra
msgid "2.399GHz"
msgstr ""
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:13
#, python-format
msgid "Cancel"
msgstr ""
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:11
#, python-format
msgid "Variant Selection of"
msgstr ""
#. module: pos_product_template
#. openerp-web
#: code:addons/pos_product_template/static/src/xml/ppt.xml:39
#: code:addons/pos_product_template/static/src/xml/ppt.xml:77
#, python-format
msgid "Variants"
msgstr ""

6
pos_product_template/readme/CONTRIBUTORS.rst

@ -0,0 +1,6 @@
* Sylvain LE GAL (https://twitter.com/legalsylvain)
* Navarromiguel (https://github.com/navarromiguel)
* Damendieta (https://github.com/damendieta)
* Raphaël Reverdy (https://akretion.com)
* Hugo Santos (https://factorlibre.com)
* Wolfgang Pichler (https://www.callino.at)

3
pos_product_template/readme/CREDITS.rst

@ -0,0 +1,3 @@
The development of this module has been financially supported by:
* Akretion

11
pos_product_template/readme/DESCRIPTION.rst

@ -0,0 +1,11 @@
* In Point Of Sale Front End - Products list:
* Display only one product per template;
* Display template name instead of product name;
* Display products quantity instead of price;
* Click on template displays an extra screen to select Variant;
* In Point Of Sale Front End - Variants list:
* Display all the products of the selected variant;
* Click on a attribute value filters products;
* Click on a product adds it to the current Order or display normal
extra screen if it is a weightable product;

1
pos_product_template/readme/ROADMAP.rst

@ -0,0 +1 @@
* Templates with lot of variants are not shown. See https://github.com/OCA/pos/pull/135

8
pos_product_template/readme/USAGE.rst

@ -0,0 +1,8 @@
Open the Point of Sale, search an article with variants.
You will see one article instead of all the variants.
#. Go to ...
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/pos/12.0

BIN
pos_product_template/static/description/icon.png

After

Width: 64  |  Height: 64  |  Size: 4.2 KiB

202
pos_product_template/static/src/css/ppt.css

@ -0,0 +1,202 @@
/******************************************************************************
Point Of Sale - Product Template module for Odoo
Copyright (C) 2014-Today Akretion (http://www.akretion.com)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
Some part of code from Odoo Project,
Copyright (C) 2004-Today Odoo SA. (<http://odoo.com>).
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
/* ********* Variant Selector PopUp ************************************** */
.pos .modal-dialog .popup-select-variant{
margin-left:0;
margin-top:0;
left:5%;
width: 90%;
top:5%;
height:85%;
}
.pos .modal-dialog .popup-select-variant .variant-title{
height: 8%;
}
.pos .modal-dialog .popup-select-variant .container-attribute-list{
width:100%;
height:40%;
overflow: hidden;
overflow-y: auto;
border-bottom: 1px dashed #444;
-webkit-overflow-scrolling: touch;
}
.pos .modal-dialog .popup-select-variant .container-variant-list{
width:100%;
height:50%;
overflow: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.pos .variant-list {
padding: 10px;
text-align: left;
-webkit-transform: translate3d(0,0,0);
}
/* ********* Attribut Display ******************************************** */
.pos .attribute {
position:relative;
width: 100%;
vertical-align: top;
display: inline-block;
}
.pos .attribute .attribute-name{
float: left;
font-size: 13px;
width: 20%;
overflow:hidden;
}
.pos .attribute .value-list-container{
width:78%;
float: left;
}
.pos .attribute .selected{
background-color: white;
}
.pos .attribute .attribute-value {
float: left;
width: 150px;
padding-left: 15px;
}
.pos .attribute .attribute-value .button{
width: 100%;
}
.pos .attribute .attribute-value .attribute-value-header{
width: 100%;
height: 15px;
text-align: right;
}
.pos .attribute .attribute-value .attribute-value-name{
width: 100%;
height: 25px;
line-height:25px;
overflow: hidden;
font-size: 10px;
text-align: left;
font-weight: normal;
padding: 2px;
}
.pos .attribute .attribute-value .variant-quantity{
position: relative;
top: 2px;
right: 0px;
vertical-align: top;
color: #FFF;
line-height: 13px;
background: none repeat scroll 0% 0% #7F82AC;
padding: 2px 5px;
border-radius: 2px;
font-size: 9px;
margin-right:3px;
}
/* ********* Variant Display ********************************************* */
/* This part is a copy paste from Odoo Project, with some values changed */
.pos .variant {
position:relative;
vertical-align: top;
display: inline-block;
line-height: 100px;
font-size: 11px;
margin: 5px !important;
width: 240px;
height:60px;
background:#fff;
border: 1px solid #e2e2e2;
border-radius: 3px;
border-bottom-width: 3px;
overflow: hidden;
cursor: pointer;
}
.pos .variant .variant-header {
position: relative;
width: 240px;
height: 30px;
background: white;
text-align: center;
}
.pos .variant .price-tag {
position: absolute;
top: 2px;
right: 2px;
vertical-align: top;
color: white;
line-height: 13px;
background: #7f82ac;
padding: 2px 5px;
border-radius: 2px;
}
.pos .variant .variant-name {
font-weight: normal;
position: absolute;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
bottom:0;
top:auto;
line-height: 14px;
width:100%;
overflow: hidden;
text-overflow: ellipsis;
background: -webkit-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
background: -moz-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
background: -ms-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
/* troublesome in latest webkit
background: linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
*/
/*background:#FFF;*/
padding: 3px;
padding-top:15px;
}
/*.pos .variant-list-scroller{*/
/* -webkit-box-sizing: border-box;*/
/* -moz-box-sizing: border-box;*/
/* -ms-box-sizing: border-box;*/
/* box-sizing: border-box;*/
/* width:50%;*/
/* height:50%;*/
/* overflow: hidden;*/
/* overflow-y: auto;*/
/* -webkit-overflow-scrolling: touch;*/
/* -webkit-transform: translate3d(0,0,0);*/
/*}*/

BIN
pos_product_template/static/src/img/screenshots/pos_product_template.png

After

Width: 1212  |  Height: 383  |  Size: 21 KiB

575
pos_product_template/static/src/js/ppt.js

@ -0,0 +1,575 @@
/* Copyright (C) 2014-Today Akretion (https://www.akretion.com)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
@author Navarromiguel (https://github.com/navarromiguel)
@author Raphaël Reverdy (https://www.akretion.com)
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
*/
odoo.define("pos_product_template.pos_product_template", function(require){
"use strict";
var screens = require("point_of_sale.screens");
var popups = require("point_of_sale.popups");
var models = require('point_of_sale.models');
var chrome = require('point_of_sale.chrome');
var gui = require('point_of_sale.gui');
var PosDB = require("point_of_sale.DB");
var PosBaseWidget = require('point_of_sale.BaseWidget');
var core = require('web.core');
var utils = require('web.utils');
var QWeb = core.qweb;
var _t = core._t;
/* ********************************************************
Overload: point_of_sale.ProductListWidget
- The overload will:
- display only product template;
- Add an extra behaviour on click on a template, if template has many
variant, displaying an extra scren to select the variant;
*********************************************************** */
var _render_product_ = screens.ProductListWidget.prototype.render_product;
screens.ProductScreenWidget.include({
click_product: function(product) {
if (product.product_variant_count > 1) {
this.gui.show_popup('select-variant-popup', product.product_tmpl_id);
} else {
this._super(product);
}
}
});
screens.ProductListWidget.include({
set_product_list: function(product_list) {
/* ************************************************
Overload: 'set_product_list'
'set_product_list' is a function called before displaying Products.
(at the beginning, after a category selection, after a research, etc.
we just remove all products that are not the 'primary variant'
*/
var list = product_list.filter(function(product) {
return product.is_primary_variant;
});
this._super(list);
},
render_product: function(product){
var self = this;
if (product.product_variant_count === 1){
// Normal Display
return this._super(product);
}
if (!product.is_primary_variant) {
//because screens.js:556: renderElement is called
//once before set_product_list
//So we get product.is_primary_variant
//which are not to be displayed
//
//Here we return mock element for
//products which will not be displayed
return document.createElement('div');
}
//TODO reuse upper function
var cached = this.product_cache.get_node(product.id);
if(!cached) {
var image_url = this.get_product_image_url(product);
var product_html = QWeb.render('Template',{
widget: this,
product: product,
image_url: this.get_product_image_url(product),
});
var product_node = document.createElement('div');
product_node.innerHTML = product_html;
product_node = product_node.childNodes[1];
this.product_cache.cache_node(product.id,product_node);
return product_node;
}
return cached;
},
});
/* ********************************************************
Overload: point_of_sale.PosWidget
- Add a new PopUp 'SelectVariantPopupWidget';
************************************************************/
chrome.Chrome.include({
/* Overload Section */
build_widgets: function(){
this._super();
this.select_variant_popup = new SelectVariantPopupWidget(this, {});
this.select_variant_popup.appendTo($(this.$el));
// Hide the popup because all pop up are displayed at the
// beginning by default
this.select_variant_popup.hide();
},
});
/* ********************************************************
Define : pos_product_template.SelectVariantPopupWidget
- This widget that display a pop up to select a variant of a Template;
***********************************************************/
var SelectVariantPopupWidget = popups.extend({
template:'SelectVariantPopupWidget',
start: function(){
var self = this;
// Define Variant Widget
this.variant_list_widget = new VariantListWidget(this,{});
this.variant_list_widget.replace(this.$('.placeholder-VariantListWidget'));
// Define Attribute Widget
this.attribute_list_widget = new AttributeListWidget(this,{});
this.attribute_list_widget.replace(this.$('.placeholder-AttributeListWidget'));
// Add behaviour on Cancel Button
this.$('#variant-popup-cancel').off('click').click(function(){
self.hide();
});
},
show: function(product_tmpl_id){
var self = this;
var template = this.pos.db.template_by_id[product_tmpl_id];
// Display Name of Template
this.$('#variant-title-name').html(template.name);
// Render Variants
var variant_ids = this.pos.db.template_by_id[product_tmpl_id].product_variant_ids;
var variant_list = variant_ids.map(function (variant) {
return this.pos.db.get_product_by_id(variant);
}, this);
this.variant_list_widget.filters = {}
this.variant_list_widget.set_variant_list(variant_list);
// Render Attributes
var attribute_ids = this.pos.db.attribute_by_template_id(template.id);
var attribute_list = attribute_ids.map(function (attribute) {
return this.pos.db.get_product_attribute_by_id(attribute);
}, this);
this.attribute_list_widget.set_attribute_list(attribute_list, template);
if(this.$el){
this.$el.removeClass('oe_hidden');
}
},
});
gui.define_popup({name:'select-variant-popup', widget: SelectVariantPopupWidget});
/* ********************************************************
Define: pos_product_template.VariantListWidget
- This widget will display a list of Variants;
- This widget has some part of code that come from point_of_sale.ProductListWidget;
***********************************************************/
var VariantListWidget = PosBaseWidget.extend({
template:'VariantListWidget',
init: function(parent, options) {
var self = this;
this._super(parent, options);
this.variant_list = [];
this.filter_variant_list = [];
this.filters = {};
this.click_variant_handler = function(event){
var variant = self.pos.db.get_product_by_id(this.dataset['variantId']);
if(variant.to_weight && self.pos.config.iface_electronic_scale){
self.__parentedParent.hide();
self.pos_widget.screen_selector.set_current_screen('scale',{product: variant});
}else{
self.__parentedParent.hide();
self.pos.get('selectedOrder').add_product(variant);
}
};
},
replace: function($target){
this.renderElement();
var target = $target[0];
target.parentNode.replaceChild(this.el,target);
},
set_filter: function(attribute_id, value_id){
this.filters[attribute_id] = value_id;
this.filter_variant();
},
reset_filter: function(attribute_id){
if (attribute_id in this.filters){
delete this.filters[attribute_id];
}
this.filter_variant();
},
filter_variant: function(){
var value_list = [];
for (var item in this.filters){
value_list.push(parseInt(this.filters[item], 10));
}
this.filter_variant_list = [];
for (var index in this.variant_list){
var variant = this.variant_list[index];
var found = true;
for (var i = 0; i < value_list.length; i++){
found = found && (variant.attribute_value_ids.indexOf(value_list[i]) !== -1);
}
if (found){
this.filter_variant_list.push(variant);
}
}
this.renderElement();
},
_get_active_pricelist: function(){
var current_order = this.pos.get_order();
var current_pricelist = this.pos.default_pricelist;
if (current_order) {
current_pricelist = current_order.pricelist;
}
return current_pricelist;
},
set_variant_list: function(variant_list){
this.variant_list = variant_list;
this.filter_variant_list = variant_list;
this.renderElement();
},
render_variant: function(variant){
var current_pricelist = this._get_active_pricelist();
var variant_html = QWeb.render('VariantWidget', {
pricelist: current_pricelist,
widget: this,
variant: variant,
});
var variant_node = document.createElement('div');
variant_node.innerHTML = variant_html;
variant_node = variant_node.childNodes[1];
return variant_node;
},
renderElement: function() {
var self = this;
var el_html = core.qweb.render(this.template, {widget: this});
var el_node = document.createElement('div');
el_node.innerHTML = el_html;
el_node = el_node.childNodes[1];
if(this.el && this.el.parentNode){
this.el.parentNode.replaceChild(el_node,this.el);
}
this.el = el_node;
var list_container = el_node.querySelector('.variant-list');
for(var i = 0, len = this.filter_variant_list.length; i < len; i++){
var variant_node = this.render_variant(this.filter_variant_list[i]);
variant_node.addEventListener('click',this.click_variant_handler);
list_container.appendChild(variant_node);
}
},
});
/* ********************************************************
Define: pos_product_template.AttributeListWidget
- This widget will display a list of Attribute;
***********************************************************/
var AttributeListWidget = PosBaseWidget.extend({
template:'AttributeListWidget',
init: function(parent, options) {
var self = this;
this.attribute_list = [];
this.product_template = null;
this.click_set_attribute_handler = function(event){
//TODO: Refactor this function with elegant DOM manipulation
// remove selected item
parent = this.parentElement.parentElement.parentElement;
parent.children[0].classList.remove('selected');
for (var i = 0 ; i < parent.children[1].children[0].children.length; i ++){
var elem = parent.children[1].children[0].children[i];
elem.children[0].classList.remove('selected');
}
// add selected item
this.children[0].classList.add('selected');
self.__parentedParent.variant_list_widget.set_filter(this.dataset['attributeId'], this.dataset['attributeValueId']);
};
this.click_reset_attribute_handler = function(event){
//TODO: Refactor this function with elegant DOM manipulation
// remove selected item
parent = this.parentElement;
parent.children[0].classList.remove('selected');
for (var i = 0 ; i < parent.children[1].children[0].children.length; i ++){
var elem = parent.children[1].children[0].children[i];
elem.children[0].classList.remove('selected');
}
// add selected item
this.classList.add('selected');
self.__parentedParent.variant_list_widget.reset_filter(this.dataset['attributeId']);
};
this._super(parent, options);
},
replace: function($target){
this.renderElement();
var target = $target[0];
target.parentNode.replaceChild(this.el,target);
},
set_attribute_list: function(attribute_list, product_template){
this.attribute_list = attribute_list;
this.product_template = product_template;
this.renderElement();
},
render_attribute: function(attribute){
var attribute_html = QWeb.render('AttributeWidget',{
widget: this,
attribute: attribute,
});
var attribute_node = document.createElement('div');
attribute_node.innerHTML = attribute_html;
attribute_node = attribute_node.childNodes[1];
var list_container = attribute_node.querySelector('.value-list');
for(var i = 0, len = attribute.value_ids.length; i < len; i++){
var value = this.pos.db.get_product_attribute_value_by_id(attribute.value_ids[i]);
var product_list = this.pos.db.get_product_by_ids(this.product_template.product_variant_ids);
var subproduct_list = this.pos.db.get_product_by_value_and_products(value.id, product_list);
var variant_qty = subproduct_list.length;
// Hide product attribute value if there is no product associated to it
if (variant_qty !== 0) {
var value_node = this.render_value(value, variant_qty);
value_node.addEventListener('click', this.click_set_attribute_handler);
list_container.appendChild(value_node);
}
};
return attribute_node;
},
render_value: function(value, variant_qty){
var value_html = QWeb.render('AttributeValueWidget',{
widget: this,
value: value,
variant_qty: variant_qty,
});
var value_node = document.createElement('div');
value_node.innerHTML = value_html;
value_node = value_node.childNodes[1];
return value_node;
},
renderElement: function() {
var self = this;
var el_html = core.qweb.render(this.template, {widget: this});
var el_node = document.createElement('div');
el_node.innerHTML = el_html;
el_node = el_node.childNodes[1];
if(this.el && this.el.parentNode){
this.el.parentNode.replaceChild(el_node,this.el);
}
this.el = el_node;
var list_container = el_node.querySelector('.attribute-list');
for(var i = 0, len = this.attribute_list.length; i < len; i++){
var attribute_node = this.render_attribute(this.attribute_list[i]);
attribute_node.querySelector('.attribute-name').addEventListener('click', this.click_reset_attribute_handler);
list_container.appendChild(attribute_node);
};
},
});
/* ********************************************************
Overload: point_of_sale.PosDB
- Add to local storage Product Templates Data.
- Link Product Variants to Product Templates.
- Add an extra field 'is_primary_variant' on product object. the product
will be display on product list, only if it is the primary variant;
Otherwise, the product will be displayed only on Template Screen.
- Add an extra field 'product_variant_count' on product object that
indicates the number of variant of the template of the product.
********************************************************** */
PosDB.include({
init: function(options){
this.template_by_id = {};
this.product_attribute_by_id = {};
this.product_attribute_value_by_id = {};
this._super(options);
},
add_products: function(products){
this._super(products);
// if pos_cache is also installed - then products are not available when product.templates are already loaded
// so we have to re add them here
if (this.raw_templates) {
this.add_templates(this.raw_templates);
}
},
get_product_by_value_and_products: function(value_id, products){
var list = [];
for (var i = 0, len = products.length; i < len; i++) {
if (products[i].attribute_value_ids.indexOf(value_id) !== -1){
list.push(products[i]);
}
}
return list;
},
get_product_attribute_by_id: function(attribute_id){
return this.product_attribute_by_id[attribute_id];
},
get_product_attribute_value_by_id: function(attribute_value_id){
return this.product_attribute_value_by_id[attribute_value_id];
},
get_product_by_ids: function(product_ids){
var list = [];
for (var i = 0, len = product_ids.length; i < len; i++) {
list.push(this.product_by_id[product_ids[i]]);
}
return list;
},
attribute_by_template_id: function(template_id){
var template = this.template_by_id[template_id];
return this.attribute_by_attribute_value_ids(template.attribute_value_ids);
},
attribute_by_attribute_value_ids: function(value_ids){
var attribute_ids = [];
for (var i = 0; i < value_ids.length; i++){
var value = this.product_attribute_value_by_id[value_ids[i]];
if (attribute_ids.indexOf(value.attribute_id[0])===-1){
attribute_ids.push(value.attribute_id[0]);
}
}
return attribute_ids;
},
add_templates: function(templates){
for(var i=0 ; i < templates.length; i++){
var attribute_value_ids = [];
// store Templates
this.template_by_id[templates[i].id] = templates[i];
// Update Product information
for (var j = 0; j <templates[i].product_variant_ids.length; j++){
var product = this.product_by_id[templates[i].product_variant_ids[j]]
for (var k = 0; k < product.attribute_value_ids.length; k++){
if (attribute_value_ids.indexOf(product.attribute_value_ids[k])===-1){
attribute_value_ids.push(product.attribute_value_ids[k]);
}
}
product.product_variant_count = templates[i].product_variant_count;
product.is_primary_variant = (j===0);
}
this.template_by_id[templates[i].id].attribute_value_ids = attribute_value_ids;
}
},
add_product_attributes: function(product_attributes){
for(var i=0 ; i < product_attributes.length; i++){
// store Product Attributes
this.product_attribute_by_id[product_attributes[i].id] = product_attributes[i];
}
},
add_product_attribute_values: function(product_attribute_values){
for(var i=0 ; i < product_attribute_values.length; i++){
// store Product Attribute Values
this.product_attribute_value_by_id[product_attribute_values[i].id] = product_attribute_values[i];
}
},
});
/*********************************************************
Overload: point_of_sale.PosModel
- Overload module.PosModel.initialize function to load extra-data
- Load 'name' field of model product.product;
- Load product.template model;
*********************************************************** */
// change product.product fields - add name and attribute_value_ids
models.PosModel.prototype.models.some(function (model) {
if (model.model !== 'product.product') {
return false;
}
// add name and attribute_value_ids to list of fields
// to fetch for product.product
['name', 'attribute_value_ids'].forEach(function (field) {
if (model.fields.indexOf(field) === -1) {
model.fields.push(field);
}
});
return true; //exit early the iteration of this.models
});
//Add our new models
models.load_models([{
model: 'product.template',
fields: [
'name',
'display_name',
'product_variant_ids',
'product_variant_count',
],
domain: function(self){
return [
['sale_ok','=',true],
['available_in_pos','=',true],
];},
context: function(self){
return {
pricelist: self.pricelists[0].id,
display_default_code: false,
};},
loaded: function(self, templates){
if (Object.keys(self.db.product_by_id).length > 0) {
self.db.add_templates(templates);
} else {
self.db.raw_templates = templates;
}
},
},
{
model: 'product.attribute',
fields: [
'name',
'value_ids',
],
loaded: function(self, attributes){
self.db.add_product_attributes(attributes);
},
},
{
model: 'product.attribute.value',
fields: [
'name',
'attribute_id',
],
loaded: function(self, values){
self.db.add_product_attribute_values(values);
},
}]);
return {
'SelectVariantPopupWidget': SelectVariantPopupWidget,
'VariantListWidget': VariantListWidget,
'AttributeListWidget': AttributeListWidget,
};
});

119
pos_product_template/static/src/xml/ppt.xml

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014-Today Akretion (https://www.akretion.com)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
<template>
<!-- Pop Up Widget -->
<t t-name="SelectVariantPopupWidget">
<div class="modal-dialog">
<div class="popup popup-select-variant">
<div class="variant-title">
Variant Selection of <span id="variant-title-name" />
<span id="variant-popup-cancel" class="button">
Cancel
</span>
</div>
<div class="content-container container-attribute-list">
<span class="placeholder-AttributeListWidget" />
</div>
<div class="content-container container-variant-list">
<span class="placeholder-VariantListWidget" />
</div>
</div>
</div>
</t>
<!-- Product Template Widget -->
<!-- This code come from Odoo Project -->
<!-- Changes:
* Display 'name' field instead of display_name field;
* Display variant quantity instead of price;
-->
<t t-name="Template">
<span class='product' t-att-data-product-id="product.id">
<div class="product-img">
<img t-att-src='image_url' />
<span class="price-tag">
<t t-esc="product.product_variant_count"/> Variants
</span>
</div>
<div class="product-name">
<t t-esc="product.name"/>
</div>
</span>
</t>
<!-- Attribute and Attributes list Widgets -->
<t t-name="AttributeListWidget">
<div class='attribute-list-container'>
<div class="attribute-list-scroller touch-scrollable">
<div class="attribute-list">
</div>
</div>
<span class="placeholder-ScrollbarWidget" />
</div>
</t>
<t t-name="AttributeWidget">
<span class='attribute' t-att-data-attribute-id="attribute.id">
<div class="attribute-name button selected" t-att-data-attribute-id="attribute.id">
<t t-esc="attribute.name"/>
</div>
<div class='value-list-container'>
<div class="value-list">
</div>
</div>
</span>
</t>
<t t-name="AttributeValueWidget">
<span class='attribute-value' t-att-data-attribute-id="value.attribute_id[0]" t-att-data-attribute-value-id="value.id">
<div class="button">
<div class="attribute-value-header">
<span class="variant-quantity">
<t t-esc="variant_qty"/> Variants
</span>
</div>
<div class="attribute-value-name">
<t t-esc="value.name"/>
</div>
</div>
</span>
</t>
<!-- Variant and Variants List Widgets -->
<t t-name="VariantListWidget">
<div class='variant-list-container'>
<div class="variant-list-scroller touch-scrollable">
<div class="variant-list">
</div>
</div>
<span class="placeholder-ScrollbarWidget" />
</div>
</t>
<t t-name="VariantWidget">
<span class='variant' t-att-data-variant-id="variant.id">
<div class="variant-header">
<t t-if="!variant.to_weight">
<span class="price-tag">
<t t-esc="widget.format_currency(variant.get_price(pricelist, 1), 'Product Price')"/>
</span>
</t>
<t t-if="variant.to_weight">
<span class="price-tag">
<t t-esc="widget.format_currency(variant.get_price(pricelist, 1), 'Product Price')+'/Kg'"/>
</span>
</t>
</div>
<div class="variant-name">
<t t-esc="variant.display_name"/>
</div>
</span>
</t>
</template>

14
pos_product_template/view/view.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014-Today Akretion (https://www.akretion.com)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
<odoo>
<template id="index" name="pos_product_template assets" inherit_id="point_of_sale.assets">
<xpath expr="//link[@id='pos-stylesheet']" position="after">
<link rel="stylesheet" href="/pos_product_template/static/src/css/ppt.css" id="pos_product_template-stylesheet"/>
</xpath>
<xpath expr="." position="inside">
<script type="text/javascript" src="/pos_product_template/static/src/js/ppt.js"></script>
</xpath>
</template>
</odoo>
Loading…
Cancel
Save