diff --git a/pos_product_template/README.rst b/pos_product_template/README.rst new file mode 100644 index 00000000..50326a42 --- /dev/null +++ b/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 +`_. 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 `_. + +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. diff --git a/pos_product_template/__init__.py b/pos_product_template/__init__.py new file mode 100644 index 00000000..95ba6a1e --- /dev/null +++ b/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 . +# +############################################################################## diff --git a/pos_product_template/__manifest__.py b/pos_product_template/__manifest__.py new file mode 100644 index 00000000..a61dacd1 --- /dev/null +++ b/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, +} diff --git a/pos_product_template/demo/product_attribute_value.xml b/pos_product_template/demo/product_attribute_value.xml new file mode 100644 index 00000000..dd1e7fba --- /dev/null +++ b/pos_product_template/demo/product_attribute_value.xml @@ -0,0 +1,31 @@ + + + + + + + 2.399GHz + + + diff --git a/pos_product_template/demo/product_product.xml b/pos_product_template/demo/product_product.xml new file mode 100644 index 00000000..a4fc71e2 --- /dev/null +++ b/pos_product_template/demo/product_product.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pos_product_template/i18n/es.po b/pos_product_template/i18n/es.po new file mode 100644 index 00000000..5d6bf4a6 --- /dev/null +++ b/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" diff --git a/pos_product_template/i18n/fr.po b/pos_product_template/i18n/fr.po new file mode 100644 index 00000000..00a21221 --- /dev/null +++ b/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" diff --git a/pos_product_template/i18n/pos_product_template.pot b/pos_product_template/i18n/pos_product_template.pot new file mode 100644 index 00000000..ab187f58 --- /dev/null +++ b/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 "" + diff --git a/pos_product_template/readme/CONTRIBUTORS.rst b/pos_product_template/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..d0bb3d93 --- /dev/null +++ b/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) \ No newline at end of file diff --git a/pos_product_template/readme/CREDITS.rst b/pos_product_template/readme/CREDITS.rst new file mode 100644 index 00000000..fdc8ca1e --- /dev/null +++ b/pos_product_template/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Akretion diff --git a/pos_product_template/readme/DESCRIPTION.rst b/pos_product_template/readme/DESCRIPTION.rst new file mode 100644 index 00000000..470b0a11 --- /dev/null +++ b/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; \ No newline at end of file diff --git a/pos_product_template/readme/ROADMAP.rst b/pos_product_template/readme/ROADMAP.rst new file mode 100644 index 00000000..34158171 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/pos_product_template/readme/USAGE.rst b/pos_product_template/readme/USAGE.rst new file mode 100644 index 00000000..9b6ae66b --- /dev/null +++ b/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 diff --git a/pos_product_template/static/description/icon.png b/pos_product_template/static/description/icon.png new file mode 100644 index 00000000..4e54d92a Binary files /dev/null and b/pos_product_template/static/description/icon.png differ diff --git a/pos_product_template/static/src/css/ppt.css b/pos_product_template/static/src/css/ppt.css new file mode 100644 index 00000000..71918c4b --- /dev/null +++ b/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. (). + + 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 . +******************************************************************************/ + +/* ********* 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);*/ + +/*}*/ diff --git a/pos_product_template/static/src/img/screenshots/pos_product_template.png b/pos_product_template/static/src/img/screenshots/pos_product_template.png new file mode 100644 index 00000000..288053e0 Binary files /dev/null and b/pos_product_template/static/src/img/screenshots/pos_product_template.png differ diff --git a/pos_product_template/static/src/js/ppt.js b/pos_product_template/static/src/js/ppt.js new file mode 100644 index 00000000..f97ecd4c --- /dev/null +++ b/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 + + diff --git a/pos_product_template/view/view.xml b/pos_product_template/view/view.xml new file mode 100644 index 00000000..770d1ad2 --- /dev/null +++ b/pos_product_template/view/view.xml @@ -0,0 +1,14 @@ + + + + +