From 80c009b1d01532d3f6be4fec4cd9b8530c4f346f Mon Sep 17 00:00:00 2001 From: Gelu Date: Tue, 11 Jul 2017 12:36:13 +0200 Subject: [PATCH] [ADD] pos_loyalty: loyalty programs for POS (#185) * [ADD] pos_loyalty: loyalty programs for POS * [FIX] Limit to one discount reward per order * [FIX] Remove unused variables and returns * [IMP] Refactoring rules loading --- pos_loyalty/README.rst | 58 +++ pos_loyalty/__init__.py | 4 + pos_loyalty/__manifest__.py | 33 ++ pos_loyalty/i18n/es.po | 467 +++++++++++++++++++++ pos_loyalty/i18n/pos_loyalty.pot | 466 ++++++++++++++++++++ pos_loyalty/models/__init__.py | 9 + pos_loyalty/models/loyalty_program.py | 31 ++ pos_loyalty/models/loyalty_reward.py | 68 +++ pos_loyalty/models/loyalty_rule.py | 33 ++ pos_loyalty/models/pos_config.py | 15 + pos_loyalty/models/pos_order.py | 31 ++ pos_loyalty/models/res_partner.py | 14 + pos_loyalty/security/ir.model.access.csv | 7 + pos_loyalty/static/src/css/pos.css | 21 + pos_loyalty/static/src/js/pos.js | 453 ++++++++++++++++++++ pos_loyalty/static/src/xml/pos.xml | 85 ++++ pos_loyalty/views/loyalty_program_view.xml | 83 ++++ pos_loyalty/views/loyalty_reward_view.xml | 37 ++ pos_loyalty/views/loyalty_rule_view.xml | 31 ++ pos_loyalty/views/pos_config_view.xml | 15 + pos_loyalty/views/pos_order_view.xml | 15 + pos_loyalty/views/res_partner_view.xml | 15 + pos_loyalty/views/templates.xml | 9 + 23 files changed, 2000 insertions(+) create mode 100644 pos_loyalty/README.rst create mode 100644 pos_loyalty/__init__.py create mode 100644 pos_loyalty/__manifest__.py create mode 100644 pos_loyalty/i18n/es.po create mode 100644 pos_loyalty/i18n/pos_loyalty.pot create mode 100644 pos_loyalty/models/__init__.py create mode 100644 pos_loyalty/models/loyalty_program.py create mode 100644 pos_loyalty/models/loyalty_reward.py create mode 100644 pos_loyalty/models/loyalty_rule.py create mode 100644 pos_loyalty/models/pos_config.py create mode 100644 pos_loyalty/models/pos_order.py create mode 100644 pos_loyalty/models/res_partner.py create mode 100644 pos_loyalty/security/ir.model.access.csv create mode 100644 pos_loyalty/static/src/css/pos.css create mode 100644 pos_loyalty/static/src/js/pos.js create mode 100644 pos_loyalty/static/src/xml/pos.xml create mode 100644 pos_loyalty/views/loyalty_program_view.xml create mode 100644 pos_loyalty/views/loyalty_reward_view.xml create mode 100644 pos_loyalty/views/loyalty_rule_view.xml create mode 100644 pos_loyalty/views/pos_config_view.xml create mode 100644 pos_loyalty/views/pos_order_view.xml create mode 100644 pos_loyalty/views/res_partner_view.xml create mode 100644 pos_loyalty/views/templates.xml diff --git a/pos_loyalty/README.rst b/pos_loyalty/README.rst new file mode 100644 index 00000000..ed9c1c7c --- /dev/null +++ b/pos_loyalty/README.rst @@ -0,0 +1,58 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=============== +Loyalty Program +=============== + +This module allows you to define a loyalty program in the point of sale, +where the customers earn loyalty points and get rewards. + +This module is a forward-port to v10 of the pos_loyalty module from Odoo's +saas-6 branch. +The functionality was moved to the Enterprise edition in later versions. + +Usage +===== + +To use this module, you need to: + +* Go to *Point of Sale > Configuration > Loyalty Programs* and define a new loyalty program with specific rules and gifts. +* Assign the loyalty program to the desired Point of Sale. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch} + +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 +======= + +Contributors +------------ + +* RGB Consulting SL (http://www.rgbconsulting.com) +* Forward-port from Odoo SA saas-6 branch + +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. \ No newline at end of file diff --git a/pos_loyalty/__init__.py b/pos_loyalty/__init__.py new file mode 100644 index 00000000..ec50cfc0 --- /dev/null +++ b/pos_loyalty/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/pos_loyalty/__manifest__.py b/pos_loyalty/__manifest__.py new file mode 100644 index 00000000..ee631c48 --- /dev/null +++ b/pos_loyalty/__manifest__.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Loyalty Program', + 'version': '10.0.1.0.0', + 'category': 'Point of Sale', + 'license': 'AGPL-3', + 'author': "OpenERP SA, " + "RGB Consulting SL, " + "Odoo Community Association (OCA)", + 'website': "https://odoo-community.org/", + 'depends': ['point_of_sale'], + + 'data': [ + 'security/ir.model.access.csv', + 'views/templates.xml', + 'views/loyalty_program_view.xml', + 'views/loyalty_reward_view.xml', + 'views/loyalty_rule_view.xml', + 'views/pos_config_view.xml', + 'views/pos_order_view.xml', + 'views/res_partner_view.xml', + ], + + 'qweb': [ + 'static/src/xml/pos.xml', + ], + + 'installable': True, +} diff --git a/pos_loyalty/i18n/es.po b/pos_loyalty/i18n/es.po new file mode 100644 index 00000000..9dc900a6 --- /dev/null +++ b/pos_loyalty/i18n/es.po @@ -0,0 +1,467 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_loyalty +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-06-23 06:36+0000\n" +"PO-Revision-Date: 2017-06-23 06:36+0000\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_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order +msgid "Amount of loyalty points given to the customer for each point of sale order" +msgstr "Cantidad de puntos de fidelidad añadidos al cliente por cada pedido del punto de venta" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product +msgid "Amount of loyalty points given to the customer per product sold" +msgstr "Cantidad de puntos de fidelidad añadidos al cliente por producto vendido" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency +msgid "Amount of loyalty points given to the customer per sold currency" +msgstr "Cantidad de puntos de fidelidad añadidos al cliente por moneda vendida" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency +msgid "Amount of points earned per currency" +msgstr "Cantidad de puntos ganados por moneda" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product +msgid "Amount of points earned per product" +msgstr "Cantidad de puntos ganados por producto" + +#. module: pos_loyalty +#: selection:loyalty.rule,type:0 +msgid "Category" +msgstr "Categoría" + +#. module: pos_loyalty +#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action +msgid "Click create to define a Loyalty Program." +msgstr "Haga clic en crear para definir un Programa de Fidelización" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost +msgid "Cost of the reward per monetary unit discounted" +msgstr "Coste de la recompensa por unidad monetaria descontada" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date +msgid "Created on" +msgstr "Creado el" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative +msgid "Cumulative" +msgstr "Cumulativo" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount +#: selection:loyalty.reward,type:0 +msgid "Discount" +msgstr "Descuento" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id +msgid "Discount Product" +msgstr "Producto de descuento" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max +msgid "Discount limit" +msgstr "Límite de descuento" + +#. module: pos_loyalty +#: code:addons/pos_loyalty/models/loyalty_reward.py:59 +#, python-format +msgid "Discount product field is mandatory for discount rewards" +msgstr "El campo Producto de descuento es obligatorio para las recompensas de tipo descuento." + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: pos_loyalty +#: selection:loyalty.reward,type:0 +msgid "Gift" +msgstr "Regalo" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id +msgid "Gift Product" +msgstr "Producto de regalo" + +#. module: pos_loyalty +#: code:addons/pos_loyalty/models/loyalty_reward.py:52 +#, python-format +msgid "Gift product field is mandatory for gift rewards" +msgstr "El campo Producto de regalo es obligatorio para las recompensas de tipo regalo" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id +msgid "ID" +msgstr "ID" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points +#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points +#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points +msgid "Loyalty Points" +msgstr "Puntos de fidelidad" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id +#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_form_view +msgid "Loyalty Program" +msgstr "Programa de fidelización" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name +msgid "Loyalty Program Name" +msgstr "Nombre programa de fidelización" + +#. module: pos_loyalty +#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action +#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view +#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view +#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view +msgid "Loyalty Programs" +msgstr "Programas de fidelización" + +#. module: pos_loyalty +#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action +msgid "Loyalty Programs allow you customers to earn points\n" +" and rewards when purchasing from your shops." +msgstr "Los programas de fidelización le permiten a los clientes ganar puntos\n" +" y recompensas al comprar en tus tiendas." + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view +msgid "Loyalty Reward" +msgstr "Recompensa de fidelización" + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view +msgid "Loyalty Rule" +msgstr "Regla de fidelización" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding +msgid "Loyalty point amounts will be rounded to multiples of this value" +msgstr "Los puntos de fidelidad se redondearán a múltiplos de este valor" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max +msgid "Maximum discounted amount allowed for this discount reward" +msgstr "Importe máximo de descuento permitido para este descuento" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points +msgid "Minimum Points" +msgstr "Puntos mínimos" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points +msgid "Minimum amount of points the customer must have to qualify for this reward" +msgstr "Cantidad mínima de puntos de cliente para acceder a esta recompensa" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/js/pos.js:365 +#, python-format +msgid "No Rewards Available" +msgstr "No hay recompensas disponibles" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_res_partner +msgid "Partner" +msgstr "Empresa" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/js/pos.js:381 +#, python-format +msgid "Please select a reward" +msgstr "Selecciona una recompensa" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost +msgid "Point Cost" +msgstr "Coste Puntos" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id +msgid "Point Product" +msgstr "Producto Punto" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_pos_order +msgid "Point of Sale Orders" +msgstr "Pedidos del TPV" + +#. module: pos_loyalty +#: code:addons/pos_loyalty/models/loyalty_reward.py:67 +#, python-format +msgid "Point product field is mandatory for point resale rewards" +msgstr "El campo Producto Punto es obligatorio para recompensas de reventa de puntos" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 +#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 +#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 +#, python-format +msgid "Points" +msgstr "Puntos" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding +msgid "Points Rounding" +msgstr "Redondeo Puntos" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 +#, python-format +msgid "Points Spent" +msgstr "Puntos Gastados" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 +#, python-format +msgid "Points Won" +msgstr "Puntos Ganados" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency +msgid "Points per currency" +msgstr "Puntos por moneda" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order +msgid "Points per order" +msgstr "Points per order" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product +msgid "Points per product" +msgstr "Puntos por producto" + +#. module: pos_loyalty +#: selection:loyalty.rule,type:0 +msgid "Product" +msgstr "Producto" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id +msgid "Product that represents a point that is sold by the customer" +msgstr "Producto que representa un punto vendido por el cliente" + +#. module: pos_loyalty +#: selection:loyalty.reward,type:0 +msgid "Resale" +msgstr "Reventa" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name +msgid "Reward Name" +msgstr "Nombre recompensa" + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +msgid "Reward the customer with gifts or discounts for loyalty points" +msgstr "Recompensar al cliente con regalos o descuentos por puntos de fidelidad" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +#, python-format +msgid "Rewards" +msgstr "Recompensas" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name +msgid "Rule Name" +msgstr "Nombre de regla" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +msgid "Rules" +msgstr "Reglas" + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +msgid "Rules define how loyalty points are earned for specific products or categories" +msgstr "Las reglas definen cómo se obtienen puntos de fidelidad para productos o categorías específicos" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id +msgid "Target Category" +msgstr "Categoría afectada" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id +msgid "Target Product" +msgstr "Producto afectado" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id +msgid "The Loyalty Program this reward belongs to" +msgstr "El programa de fidelización al que pertenece esta recompensa" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id +msgid "The Loyalty Program this rule belongs to" +msgstr "El programa de fidelización al que pertenece esta regla" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points +msgid "The amount of Loyalty points awarded to the customer with this order" +msgstr "La cantidad de puntos de fidelidad que el cliente obtiene con este pedido" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id +msgid "The category affected by this rule" +msgstr "La categoría afectada por esta regla" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type +msgid "The concept this rule applies to" +msgstr "El concepto al que aplica esta regla" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount +msgid "The discount percentage" +msgstr "El porcentaje de descuento" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points +#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points +msgid "The loyalty points the user won as part of a Loyalty Program" +msgstr "Los puntos de fidelidad que el usuario ganó como parte de un Programa de Fidelización" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id +msgid "The loyalty program used by this Point of Sale" +msgstr "El programa de fidelización utilizado en este Punto de Venta" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative +msgid "The points from this rule will be added to points won from other rules with the same concept" +msgstr "Los puntos de esta regla se añadirán a los puntos ganados en otras reglas con el mismo concepto" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id +msgid "The product affected by this rule" +msgstr "El producto afectado por esta regla" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id +msgid "The product given as a reward" +msgstr "El producto utilizado como recompensa" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id +msgid "The product used to apply discounts" +msgstr "El producto utilizado para aplicar descuentos" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/js/pos.js:366 +#, python-format +msgid "There are no rewards available for this customer as part of the loyalty program" +msgstr "No hay recompensas disponibles para este cliente como parte de este programa de fidelización" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 +#, python-format +msgid "Total Points" +msgstr "Total Puntos" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type +msgid "Type" +msgstr "Tipo" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type +msgid "Type of the reward" +msgstr "Tipo de recompensa" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_loyalty_program +msgid "loyalty.program" +msgstr "loyalty.program" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_loyalty_reward +msgid "loyalty.reward" +msgstr "loyalty.reward" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_loyalty_rule +msgid "loyalty.rule" +msgstr "loyalty.rule" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_pos_config +msgid "pos.config" +msgstr "Configuración del TPV" + diff --git a/pos_loyalty/i18n/pos_loyalty.pot b/pos_loyalty/i18n/pos_loyalty.pot new file mode 100644 index 00000000..b4a67959 --- /dev/null +++ b/pos_loyalty/i18n/pos_loyalty.pot @@ -0,0 +1,466 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_loyalty +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-06-23 06:35+0000\n" +"PO-Revision-Date: 2017-06-23 06:35+0000\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_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order +msgid "Amount of loyalty points given to the customer for each point of sale order" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product +msgid "Amount of loyalty points given to the customer per product sold" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency +msgid "Amount of loyalty points given to the customer per sold currency" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency +msgid "Amount of points earned per currency" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product +msgid "Amount of points earned per product" +msgstr "" + +#. module: pos_loyalty +#: selection:loyalty.rule,type:0 +msgid "Category" +msgstr "" + +#. module: pos_loyalty +#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action +msgid "Click create to define a Loyalty Program." +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost +msgid "Cost of the reward per monetary unit discounted" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid +msgid "Created by" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date +msgid "Created on" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative +msgid "Cumulative" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount +#: selection:loyalty.reward,type:0 +msgid "Discount" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id +msgid "Discount Product" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max +msgid "Discount limit" +msgstr "" + +#. module: pos_loyalty +#: code:addons/pos_loyalty/models/loyalty_reward.py:59 +#, python-format +msgid "Discount product field is mandatory for discount rewards" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name +msgid "Display Name" +msgstr "" + +#. module: pos_loyalty +#: selection:loyalty.reward,type:0 +msgid "Gift" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id +msgid "Gift Product" +msgstr "" + +#. module: pos_loyalty +#: code:addons/pos_loyalty/models/loyalty_reward.py:52 +#, python-format +msgid "Gift product field is mandatory for gift rewards" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id +msgid "ID" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update +msgid "Last Modified on" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date +msgid "Last Updated on" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points +#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points +#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points +msgid "Loyalty Points" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id +#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_form_view +msgid "Loyalty Program" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name +msgid "Loyalty Program Name" +msgstr "" + +#. module: pos_loyalty +#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action +#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view +#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view +#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view +msgid "Loyalty Programs" +msgstr "" + +#. module: pos_loyalty +#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action +msgid "Loyalty Programs allow you customers to earn points\n" +" and rewards when purchasing from your shops." +msgstr "" + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view +msgid "Loyalty Reward" +msgstr "" + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view +msgid "Loyalty Rule" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding +msgid "Loyalty point amounts will be rounded to multiples of this value" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max +msgid "Maximum discounted amount allowed for this discount reward" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points +msgid "Minimum Points" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points +msgid "Minimum amount of points the customermust have to qualify for this reward" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/js/pos.js:365 +#, python-format +msgid "No Rewards Available" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_res_partner +msgid "Partner" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/js/pos.js:381 +#, python-format +msgid "Please select a reward" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost +msgid "Point Cost" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id +msgid "Point Product" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_pos_order +msgid "Point of Sale Orders" +msgstr "" + +#. module: pos_loyalty +#: code:addons/pos_loyalty/models/loyalty_reward.py:67 +#, python-format +msgid "Point product field is mandatory for point resale rewards" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 +#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 +#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 +#, python-format +msgid "Points" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding +msgid "Points Rounding" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 +#, python-format +msgid "Points Spent" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 +#, python-format +msgid "Points Won" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency +msgid "Points per currency" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order +msgid "Points per order" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product +msgid "Points per product" +msgstr "" + +#. module: pos_loyalty +#: selection:loyalty.rule,type:0 +msgid "Product" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id +msgid "Product that represents a point that is sold by the customer" +msgstr "" + +#. module: pos_loyalty +#: selection:loyalty.reward,type:0 +msgid "Resale" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name +msgid "Reward Name" +msgstr "" + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +msgid "Reward the customer with gifts or discounts for loyalty points" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +#, python-format +msgid "Rewards" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name +msgid "Rule Name" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +msgid "Rules" +msgstr "" + +#. module: pos_loyalty +#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view +msgid "Rules define how loyalty points are earned for specific products or categories" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id +msgid "Target Category" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id +msgid "Target Product" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id +msgid "The Loyalty Program this reward belongs to" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id +msgid "The Loyalty Program this rule belongs to" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points +msgid "The amount of Loyalty points awarded to the customer with this order" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id +msgid "The category affected by this rule" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type +msgid "The concept this rule applies to" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount +msgid "The discount percentage" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points +#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points +msgid "The loyalty points the user won as part of a Loyalty Program" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id +msgid "The loyalty program used by this Point of Sale" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative +msgid "The points from this rule will be added to points won from other rules with the same concept" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id +msgid "The product affected by this rule" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id +msgid "The product given as a reward" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id +msgid "The product used to apply discounts" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/js/pos.js:366 +#, python-format +msgid "There are no rewards available for this customer as part of the loyalty program" +msgstr "" + +#. module: pos_loyalty +#. openerp-web +#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 +#, python-format +msgid "Total Points" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type +#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type +msgid "Type" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type +msgid "Type of the reward" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_loyalty_program +msgid "loyalty.program" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_loyalty_reward +msgid "loyalty.reward" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_loyalty_rule +msgid "loyalty.rule" +msgstr "" + +#. module: pos_loyalty +#: model:ir.model,name:pos_loyalty.model_pos_config +msgid "pos.config" +msgstr "" + diff --git a/pos_loyalty/models/__init__.py b/pos_loyalty/models/__init__.py new file mode 100644 index 00000000..849d26de --- /dev/null +++ b/pos_loyalty/models/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import loyalty_program +from . import loyalty_reward +from . import loyalty_rule +from . import pos_config +from . import pos_order +from . import res_partner diff --git a/pos_loyalty/models/loyalty_program.py b/pos_loyalty/models/loyalty_program.py new file mode 100644 index 00000000..5817f6d3 --- /dev/null +++ b/pos_loyalty/models/loyalty_program.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class LoyaltyProgram(models.Model): + _name = 'loyalty.program' + + name = fields.Char(string='Loyalty Program Name', size=32, index=True, + required=True) + pp_currency = fields.Float(string='Points per currency', + help='Amount of loyalty points given to the ' + 'customer per sold currency') + pp_product = fields.Float(string='Points per product', + help='Amount of loyalty points given to the ' + 'customer per product sold') + pp_order = fields.Float(string='Points per order', + help='Amount of loyalty points given to the ' + 'customer for each point of sale order') + rounding = fields.Float(string='Points Rounding', default=1, + help='Loyalty point amounts will be rounded to ' + 'multiples of this value') + rule_ids = fields.One2many(comodel_name='loyalty.rule', + inverse_name='loyalty_program_id', + string='Rules') + reward_ids = fields.One2many(comodel_name='loyalty.reward', + inverse_name='loyalty_program_id', + string='Rewards') diff --git a/pos_loyalty/models/loyalty_reward.py b/pos_loyalty/models/loyalty_reward.py new file mode 100644 index 00000000..d0897171 --- /dev/null +++ b/pos_loyalty/models/loyalty_reward.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models, api, _ +from odoo.exceptions import ValidationError + + +class LoyaltyReward(models.Model): + _name = 'loyalty.reward' + + name = fields.Char(string='Reward Name', size=32, index=True, + required=True) + type = fields.Selection(selection=[('gift', 'Gift'), + ('discount', 'Discount'), + ('resale', 'Resale')], + string='Type', required=True, + help='Type of the reward') + minimum_points = fields.Float(string='Minimum Points', + help='Minimum amount of points the customer' + ' must have to qualify for this reward') + point_cost = fields.Float(string='Point Cost', + help='Cost of the reward per monetary unit ' + 'discounted') + discount = fields.Float(help='The discount percentage') + discount_max = fields.Float(string='Discount limit', + help='Maximum discounted amount allowed for' + 'this discount reward') + loyalty_program_id = fields.Many2one(comodel_name='loyalty.program', + string='Loyalty Program', + help='The Loyalty Program this reward' + ' belongs to') + gift_product_id = fields.Many2one(comodel_name='product.product', + string='Gift Product', + help='The product given as a reward') + discount_product_id = fields.Many2one(comodel_name='product.product', + string='Discount Product', + help='The product used to apply ' + 'discounts') + point_product_id = fields.Many2one(comodel_name='product.product', + string='Point Product', + help='Product that represents a point ' + 'that is sold by the customer') + + @api.multi + @api.constrains('type', 'gift_product_id') + def _check_gift_product(self): + for reward in self: + if reward.type == 'gift' and not reward.gift_product_id: + raise ValidationError( + _('Gift product field is mandatory for gift rewards')) + + @api.multi + @api.constrains('type', 'discount_product_id') + def _check_discount_product(self): + for reward in self: + if reward.type == 'discount' and not reward.discount_product_id: + raise ValidationError(_('Discount product field is ' + 'mandatory for discount rewards')) + + @api.multi + @api.constrains('type', 'point_product_id') + def _check_point_product(self): + for reward in self: + if reward.type == 'resale' and not reward.point_product_id: + raise ValidationError(_('Point product field is ' + 'mandatory for point resale rewards')) diff --git a/pos_loyalty/models/loyalty_rule.py b/pos_loyalty/models/loyalty_rule.py new file mode 100644 index 00000000..9f3b43bb --- /dev/null +++ b/pos_loyalty/models/loyalty_rule.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class LoyaltyRule(models.Model): + _name = 'loyalty.rule' + + name = fields.Char(string='Rule Name', size=32, index=True, required=True) + type = fields.Selection(selection=[('product', 'Product'), + ('category', 'Category')], + string='Type', required=True, default='product', + help='The concept this rule applies to') + cumulative = fields.Boolean(help='The points from this rule will be added ' + 'to points won from other rules with ' + 'the same concept') + pp_product = fields.Float(string='Points per product', + help='Amount of points earned per product') + pp_currency = fields.Float(string='Points per currency', + help='Amount of points earned per currency') + loyalty_program_id = fields.Many2one(comodel_name='loyalty.program', + string='Loyalty Program', + help='The Loyalty Program this rule ' + 'belongs to') + product_id = fields.Many2one(comodel_name='product.product', + string='Target Product', + help='The product affected by this rule') + category_id = fields.Many2one(comodel_name='pos.category', + string='Target Category', + help='The category affected by this rule') diff --git a/pos_loyalty/models/pos_config.py b/pos_loyalty/models/pos_config.py new file mode 100644 index 00000000..bf91f5e5 --- /dev/null +++ b/pos_loyalty/models/pos_config.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class PosConfig(models.Model): + _inherit = 'pos.config' + + loyalty_id = fields.Many2one(comodel_name='loyalty.program', + string='Loyalty Program', + help='The loyalty program used by this ' + 'Point of Sale') diff --git a/pos_loyalty/models/pos_order.py b/pos_loyalty/models/pos_order.py new file mode 100644 index 00000000..f8eed30e --- /dev/null +++ b/pos_loyalty/models/pos_order.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models, api + + +class PosOrder(models.Model): + _inherit = 'pos.order' + + loyalty_points = fields.Float(string='Loyalty Points', + help='The amount of Loyalty points awarded ' + 'to the customer with this order') + + @api.model + def _order_fields(self, ui_order): + res = super(PosOrder, self)._order_fields(ui_order) + res['loyalty_points'] = ui_order.get('loyalty_points', 0) + return res + + @api.model + def create_from_ui(self, orders): + res = super(PosOrder, self).create_from_ui(orders) + for order in orders: + order_partner = order['data']['partner_id'] + order_points = order['data']['loyalty_points'] + if order_points != 0 and order_partner: + partner = self.env['res.partner'].browse(order_partner) + partner.loyalty_points += order_points + return res diff --git a/pos_loyalty/models/res_partner.py b/pos_loyalty/models/res_partner.py new file mode 100644 index 00000000..d678db55 --- /dev/null +++ b/pos_loyalty/models/res_partner.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + loyalty_points = fields.Float(string='Loyalty Points', + help='The loyalty points the user won as ' + 'part of a Loyalty Program') diff --git a/pos_loyalty/security/ir.model.access.csv b/pos_loyalty/security/ir.model.access.csv new file mode 100644 index 00000000..8c13e017 --- /dev/null +++ b/pos_loyalty/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_loyalty_program,loyalty.program.user,model_loyalty_program,point_of_sale.group_pos_user,1,0,0,0 +access_loyalty_program_manager,loyalty.program.manager,model_loyalty_program,point_of_sale.group_pos_manager,1,1,1,1 +access_loyalty_rule,loyalty.rule.user,model_loyalty_rule,point_of_sale.group_pos_user,1,0,0,0 +access_loyalty_rule_manager,loyalty.rule.manager,model_loyalty_rule,point_of_sale.group_pos_manager,1,1,1,1 +access_loyalty_reward,loyalty.reward.user,model_loyalty_reward,point_of_sale.group_pos_user,1,0,0,0 +access_loyalty_reward_manager,loyalty.reward.manager,model_loyalty_reward,point_of_sale.group_pos_manager,1,1,1,1 diff --git a/pos_loyalty/static/src/css/pos.css b/pos_loyalty/static/src/css/pos.css new file mode 100644 index 00000000..4d141918 --- /dev/null +++ b/pos_loyalty/static/src/css/pos.css @@ -0,0 +1,21 @@ + +.pos .order .summary .loyalty-points{ + margin-left: 20px; + float: left; + padding: 10px; + max-width: 216px; + text-align: left; + color: #6EC89B; + background: rgba(110, 200, 155, 0.17); + border-radius: 3px; +} +.pos .order .summary .loyalty-points.negative{ + color: #C86E6E; + background: rgba(200, 110, 110, 0.17); +} +.pos .order .summary .loyalty-points-total { + border-top: solid 2px; + text-align: center; + padding-top: 4px; + margin-top: 4px; +} diff --git a/pos_loyalty/static/src/js/pos.js b/pos_loyalty/static/src/js/pos.js new file mode 100644 index 00000000..9a6a8936 --- /dev/null +++ b/pos_loyalty/static/src/js/pos.js @@ -0,0 +1,453 @@ +/* Copyright 2004-2010 OpenERP SA + * Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ + +odoo.define('pos_loyalty.loyalty_program', function (require){ +"use strict" + + var models = require('point_of_sale.models'); + var screens = require('point_of_sale.screens'); + + var utils = require('web.utils'); + var round_pr = utils.round_precision; + + var core = require('web.core'); + var QWeb = core.qweb; + var _t = core._t; + + models.load_fields('res.partner','loyalty_points'); + + models.load_models([ + { + model: 'loyalty.program', + condition: function(self){ return !!self.config.loyalty_id[0]; }, + fields: ['name','pp_currency','pp_product','pp_order','rounding'], + domain: function(self){ return [['id','=',self.config.loyalty_id[0]]]; }, + loaded: function(self,loyalties){ + self.loyalty = loyalties[0]; + }, + },{ + model: 'loyalty.rule', + condition: function(self){ return !!self.loyalty; }, + fields: ['name','type','product_id','category_id','cumulative','pp_product','pp_currency'], + domain: function(self){ return [['loyalty_program_id','=',self.loyalty.id]]; }, + loaded: function(self,rules){ + + self.loyalty.rules = rules; + self.loyalty.rules_by_product_id = {}; + self.loyalty.rules_by_category_id = {}; + + function update_rules(rules, rule, id) { + if (!rules[id]){ + rules[id] = [rule]; + } else if (rule.cumulative){ + rules[id].unshift(rule); + } else { + rules[id].push(rule); + } + } + + _.each(rules, function(rule) { + if (rule.type === 'product') + update_rules(self.loyalty.rules_by_product_id, rule, rule.product_id[0]) + else if (rule.type === 'category') + update_rules(self.loyalty.rules_by_category_id, rule, rule.category_id[0]); + }); + }, + },{ + model: 'loyalty.reward', + condition: function(self){ return !!self.loyalty; }, + fields: ['name','type','minimum_points','gift_product_id','point_cost','discount_product_id','discount','discount_max','point_product_id'], + domain: function(self){ return [['loyalty_program_id','=',self.loyalty.id]]; }, + loaded: function(self,rewards){ + self.loyalty.rewards = rewards; + self.loyalty.rewards_by_id = {}; + for (var i = 0; i < rewards.length;i++) { + self.loyalty.rewards_by_id[rewards[i].id] = rewards[i]; + } + }, + }, + ],{'after': 'product.product'}); + + var _orderline_super = models.Orderline.prototype; + models.Orderline = models.Orderline.extend({ + get_reward: function(){ + return this.pos.loyalty.rewards_by_id[this.reward_id]; + }, + set_reward: function(reward){ + this.reward_id = reward.id; + }, + export_as_JSON: function(){ + var json = _orderline_super.export_as_JSON.apply(this, arguments); + json.reward_id = this.reward_id; + return json; + }, + init_from_JSON: function(json){ + _orderline_super.init_from_JSON.apply(this, arguments); + this.reward_id = json.reward_id; + }, + }); + + var _order_super = models.Order.prototype; + models.Order = models.Order.extend({ + + /* The total of points won, excluding the points spent on rewards */ + get_won_points: function(){ + if (!this.pos.loyalty || !this.get_client()) { + return 0; + } + + var orderLines = this.get_orderlines(); + var rounding = this.pos.loyalty.rounding; + + var product_sold = 0; + var total_sold = 0; + var total_points = 0; + + for (var i = 0; i < orderLines.length; i++) { + var line = orderLines[i]; + var product = line.get_product(); + var rules = this.pos.loyalty.rules_by_product_id[product.id] || []; + var overriden = false; + + if (line.get_reward()) { // Reward products are ignored + continue; + } + + for (var j = 0; j < rules.length; j++) { + var rule = rules[j]; + total_points += round_pr(line.get_quantity() * rule.pp_product, rounding); + total_points += round_pr(line.get_price_with_tax() * rule.pp_currency, rounding); + // if affected by a non cumulative rule, skip the others. (non cumulative rules are put + // at the beginning of the list when they are loaded ) + if (!rule.cumulative) { + overriden = true; + break; + } + } + + // Test the category rules + if ( product.pos_categ_id ) { + var category = this.pos.db.get_category_by_id(product.pos_categ_id[0]); + while (category && !overriden) { + var rules = this.pos.loyalty.rules_by_category_id[category.id] || []; + for (var j = 0; j < rules.length; j++) { + var rule = rules[j]; + total_points += round_pr(line.get_quantity() * rule.pp_product, rounding); + total_points += round_pr(line.get_price_with_tax() * rule.pp_currency, rounding); + if (!rule.cumulative) { + overriden = true; + break; + } + } + var _category = category; + category = this.pos.db.get_category_by_id(this.pos.db.get_category_parent_id(category.id)); + if (_category === category) { + break; + } + } + } + + if (!overriden) { + product_sold += line.get_quantity(); + total_sold += line.get_price_with_tax(); + } + } + + total_points += round_pr( total_sold * this.pos.loyalty.pp_currency, rounding ); + total_points += round_pr( product_sold * this.pos.loyalty.pp_product, rounding ); + total_points += round_pr( this.pos.loyalty.pp_order, rounding ); + + return total_points; + }, + + /* The total number of points spent on rewards */ + get_spent_points: function() { + if (!this.pos.loyalty || !this.get_client()) { + return 0; + } else { + var lines = this.get_orderlines(); + var rounding = this.pos.loyalty.rounding; + var points = 0; + + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var reward = line.get_reward(); + if (reward) { + if (reward.type === 'gift') { + points += round_pr(line.get_quantity() * reward.point_cost, rounding); + } else if (reward.type === 'discount') { + points += reward.point_cost; + } else if (reward.type === 'resale') { + points += (-line.get_quantity()); + } + } + } + + return points; + } + }, + + /* The total number of points lost or won after the order is validated */ + get_new_points: function() { + if (!this.pos.loyalty || !this.get_client()) { + return 0; + } else { + return round_pr(this.get_won_points() - this.get_spent_points(), this.pos.loyalty.rounding); + } + }, + + /* The total number of points that the customer will have after this order is validated */ + get_new_total_points: function() { + if (!this.pos.loyalty || !this.get_client()) { + return 0; + } else { + return round_pr(this.get_client().loyalty_points + this.get_new_points(), this.pos.loyalty.rounding); + } + }, + + /* The number of loyalty points currently owned by the customer */ + get_current_points: function(){ + return this.get_client() ? this.get_client().loyalty_points : 0; + }, + + /* The total number of points spendable on rewards */ + get_spendable_points: function(){ + if (!this.pos.loyalty || !this.get_client()) { + return 0; + } else { + return round_pr(this.get_client().loyalty_points - this.get_spent_points(), this.pos.loyalty.rounding); + } + }, + + has_discount_reward: function(){ + var res = false; + var lines = this.get_orderlines(); + + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var reward = line.get_reward(); + if (reward && reward.type === 'discount') { + res = true; + break; + } + } + return res; + }, + + /* The list of rewards that the current customer can get */ + get_available_rewards: function(){ + var client = this.get_client(); + if (!client) { + return []; + } + var rewards = []; + var discount_reward_set = this.has_discount_reward(); + for (var i = 0; i < this.pos.loyalty.rewards.length; i++) { + var reward = this.pos.loyalty.rewards[i]; + + if (reward.minimum_points > this.get_spendable_points()) { + continue; + } else if(reward.type === 'gift' && + reward.point_cost > this.get_spendable_points()) { + continue; + } else if(reward.type === 'discount' && + (discount_reward_set || reward.point_cost > this.get_spendable_points())){ + continue; + } + rewards.push(reward); + } + return rewards; + }, + + apply_reward: function(reward){ + var client = this.get_client(); + if (!client) { + return; + } else if (reward.type === 'gift') { + var product = this.pos.db.get_product_by_id(reward.gift_product_id[0]); + + if (!product) { + return; + } + + this.add_product(product, { + price: 0, + quantity: 1, + merge: false, + extras: { reward_id: reward.id }, + }); + + } else if (reward.type === 'discount') { + + var crounding = this.pos.currency.rounding; + var order_total = this.get_total_with_tax(); + var discount = round_pr(order_total * reward.discount,crounding); + var discount_max = reward.discount_max + + if (discount_max && discount > discount_max ) { + discount = discount_max; + } + + var product = this.pos.db.get_product_by_id(reward.discount_product_id[0]); + + if (!product) { + return; + } + + this.add_product(product, { + price: -discount, + quantity: 1, + merge: false, + extras: { reward_id: reward.id }, + }); + + } else if (reward.type === 'resale') { + + var lrounding = this.pos.loyalty.rounding; + var crounding = this.pos.currency.rounding; + var spendable = this.get_spendable_points(); + + var order_total = this.get_total_with_tax(); + var product = this.pos.db.get_product_by_id(reward.point_product_id[0]); + + if (!product) { + return; + } + + if ( round_pr( spendable * product.price, crounding ) > order_total ) { + spendable = round_pr( Math.floor(order_total / product.price), lrounding); + } + + if ( spendable < 0.00001 ) { + return; + } + + this.add_product(product, { + quantity: -spendable, + merge: false, + extras: { reward_id: reward.id }, + }); + } + }, + + finalize: function(){ + var client = this.get_client(); + if ( client ) { + client.loyalty_points = this.get_new_total_points(); + this.pos.gui.screen_instances.clientlist.partner_cache.clear_node(client.id); + } + _order_super.finalize.apply(this, arguments); + }, + + export_for_printing: function(){ + var json = _order_super.export_for_printing.apply(this, arguments); + if (this.pos.loyalty && this.get_client()) { + json.loyalty = { + rounding: this.pos.loyalty.rounding || 1, + name: this.pos.loyalty.name, + client: this.get_client().name, + points_won : this.get_won_points(), + points_spent: this.get_spent_points(), + points_total: this.get_new_total_points(), + }; + } + return json; + }, + + export_as_JSON: function(){ + var json = _order_super.export_as_JSON.apply(this, arguments); + json.loyalty_points = this.get_new_points(); + return json; + }, + }); + + var LoyaltyButton = screens.ActionButtonWidget.extend({ + template: 'LoyaltyButton', + button_click: function(){ + var self = this; + var order = this.pos.get_order(); + var client = order.get_client(); + if (!client) { + this.gui.show_screen('clientlist'); + return; + } + + var rewards = order.get_available_rewards(); + if (rewards.length === 0) { + this.gui.show_popup('error',{ + 'title': _t('No Rewards Available'), + 'body': _t('There are no rewards available for this customer as part of the loyalty program'), + }); + } else if (rewards.length === 1 && this.pos.loyalty.rewards.length === 1) { + order.apply_reward(rewards[0]); + } else { + var list = []; + for (var i = 0; i < rewards.length; i++) { + list.push({ + label: rewards[i].name, + item: rewards[i], + }); + } + this.gui.show_popup('selection',{ + 'title': _t('Please select a reward'), + 'list': list, + 'confirm': function(reward){ + order.apply_reward(reward); + }, + }); + } + }, + }); + + screens.define_action_button({ + 'name': 'loyalty', + 'widget': LoyaltyButton, + 'condition': function(){ + return this.pos.loyalty && this.pos.loyalty.rewards.length; + }, + }); + + screens.OrderWidget.include({ + update_summary: function(){ + this._super(); + + var order = this.pos.get_order(); + + var $loypoints = $(this.el).find('.summary .loyalty-points'); + + if(this.pos.loyalty && order.get_client()){ + var points_won = order.get_won_points(); + var points_spent = order.get_spent_points(); + var points_total = order.get_new_total_points(); + $loypoints.replaceWith($(QWeb.render('LoyaltyPoints',{ + widget: this, + rounding: this.pos.loyalty.rounding, + points_won: points_won, + points_spent: points_spent, + points_total: points_total, + }))); + $loypoints = $(this.el).find('.summary .loyalty-points'); + $loypoints.removeClass('oe_hidden'); + + if(points_total < 0){ + $loypoints.addClass('negative'); + }else{ + $loypoints.removeClass('negative'); + } + }else{ + $loypoints.empty(); + $loypoints.addClass('oe_hidden'); + } + + if (this.pos.loyalty && + order.get_client() && + this.getParent().action_buttons && + this.getParent().action_buttons.loyalty) { + + var rewards = order.get_available_rewards(); + this.getParent().action_buttons.loyalty.highlight(!!rewards.length); + } + }, + }); +}); diff --git a/pos_loyalty/static/src/xml/pos.xml b/pos_loyalty/static/src/xml/pos.xml new file mode 100644 index 00000000..988096db --- /dev/null +++ b/pos_loyalty/static/src/xml/pos.xml @@ -0,0 +1,85 @@ + + + + +
+
Points
+ +
+ + +
+
+ +
+ - +
+
+
+ +
+
+
+ + +
+ Rewards +
+
+ + + +
.
+
+
+ + + + + + + + + + + + +
+
--------------------------------
+
+
+
+
+
+ + Points Won + + + Points Spent + + + Total Points + +
+
+
+
+
+ + + +
+ Points + + + +
+
+
+ + + + Points + + + +
diff --git a/pos_loyalty/views/loyalty_program_view.xml b/pos_loyalty/views/loyalty_program_view.xml new file mode 100644 index 00000000..65f3d247 --- /dev/null +++ b/pos_loyalty/views/loyalty_program_view.xml @@ -0,0 +1,83 @@ + + + + loyalty.program.form + loyalty.program + +
+ +
+
+ + + + + + + + + + + + + +

Rules define how loyalty points are earned for specific products or categories

+ + + + + + + + + + +

Reward the customer with gifts or discounts for loyalty points

+ + + + + + +
+
+
+
+ + + loyalty.program.tree + loyalty.program + + + + + + + + + Loyalty Programs + ir.actions.act_window + loyalty.program + form + tree,form + +

+ Click create to define a Loyalty Program. +

+

+ Loyalty Programs allow you customers to earn points + and rewards when purchasing from your shops. +

+
+
+ + +
diff --git a/pos_loyalty/views/loyalty_reward_view.xml b/pos_loyalty/views/loyalty_reward_view.xml new file mode 100644 index 00000000..f194ccac --- /dev/null +++ b/pos_loyalty/views/loyalty_reward_view.xml @@ -0,0 +1,37 @@ + + + + loyalty.reward.form + loyalty.reward + +
+
+
+ + + + + + + + + + + + + + +
+
+
+
diff --git a/pos_loyalty/views/loyalty_rule_view.xml b/pos_loyalty/views/loyalty_rule_view.xml new file mode 100644 index 00000000..bf1152d9 --- /dev/null +++ b/pos_loyalty/views/loyalty_rule_view.xml @@ -0,0 +1,31 @@ + + + + loyalty.rule.form + loyalty.rule + +
+
+
+ + + + + + + + + + + + + + +
+
+
+
diff --git a/pos_loyalty/views/pos_config_view.xml b/pos_loyalty/views/pos_config_view.xml new file mode 100644 index 00000000..525f19f2 --- /dev/null +++ b/pos_loyalty/views/pos_config_view.xml @@ -0,0 +1,15 @@ + + + + pos.config.form + pos.config + + + + + + + + + + diff --git a/pos_loyalty/views/pos_order_view.xml b/pos_loyalty/views/pos_order_view.xml new file mode 100644 index 00000000..225f610a --- /dev/null +++ b/pos_loyalty/views/pos_order_view.xml @@ -0,0 +1,15 @@ + + + + pos.order.form + pos.order + + + + + + + + + + diff --git a/pos_loyalty/views/res_partner_view.xml b/pos_loyalty/views/res_partner_view.xml new file mode 100644 index 00000000..a768ead9 --- /dev/null +++ b/pos_loyalty/views/res_partner_view.xml @@ -0,0 +1,15 @@ + + + + partner.property.form + res.partner + + + + + + + + + + diff --git a/pos_loyalty/views/templates.xml b/pos_loyalty/views/templates.xml new file mode 100644 index 00000000..d05d64a8 --- /dev/null +++ b/pos_loyalty/views/templates.xml @@ -0,0 +1,9 @@ + + + +