From 987bc5baf5a36d20c79b33581a47397747f2095d Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Mon, 25 Sep 2017 14:41:51 +0200 Subject: [PATCH 01/94] [ADD] sale_order_product_recommendation: SO product wizard This wizard that you can see now in your quotations will let you add products quickly based on the customer's recent purchase history. --- sale_order_product_recommendation/README.rst | 67 +++++++ sale_order_product_recommendation/__init__.py | 4 + .../__openerp__.py | 21 +++ sale_order_product_recommendation/i18n/es.po | 170 +++++++++++++++++ .../static/description/icon.png | Bin 0 -> 9455 bytes .../tests/__init__.py | 4 + .../tests/test_recommendation.py | 100 ++++++++++ .../views/sale_order_view.xml | 22 +++ .../wizards/__init__.py | 4 + .../wizards/sale_order_recommendation.py | 174 ++++++++++++++++++ .../sale_order_recommendation_view.xml | 53 ++++++ 11 files changed, 619 insertions(+) create mode 100644 sale_order_product_recommendation/README.rst create mode 100644 sale_order_product_recommendation/__init__.py create mode 100644 sale_order_product_recommendation/__openerp__.py create mode 100644 sale_order_product_recommendation/i18n/es.po create mode 100644 sale_order_product_recommendation/static/description/icon.png create mode 100644 sale_order_product_recommendation/tests/__init__.py create mode 100644 sale_order_product_recommendation/tests/test_recommendation.py create mode 100644 sale_order_product_recommendation/views/sale_order_view.xml create mode 100644 sale_order_product_recommendation/wizards/__init__.py create mode 100644 sale_order_product_recommendation/wizards/sale_order_recommendation.py create mode 100644 sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml diff --git a/sale_order_product_recommendation/README.rst b/sale_order_product_recommendation/README.rst new file mode 100644 index 00000000000..90944302469 --- /dev/null +++ b/sale_order_product_recommendation/README.rst @@ -0,0 +1,67 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +================================= +Sale Order Product Recommendation +================================= + +This module adds a recommended products wizard to current sale order. + +It is based on recent delivered products, and allows the salesman to quickly +know the most sold products for current customer, which results in an easy to +use hint to improve sale. + +Usage +===== + +To use this module, you need to: + +#. Create a new sale order. +#. Assign its customer. +#. Press *Recommended Products* button. +#. Add products into the opened wizard. +#. Press *Accept*. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/167/9.0 + +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 +------------ + +* `Tecnativa `_: + * Jairo Llopis + +Do not contact contributors directly about support or help with technical issues. + +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/sale_order_product_recommendation/__init__.py b/sale_order_product_recommendation/__init__.py new file mode 100644 index 00000000000..0796ed1dec6 --- /dev/null +++ b/sale_order_product_recommendation/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import wizards diff --git a/sale_order_product_recommendation/__openerp__.py b/sale_order_product_recommendation/__openerp__.py new file mode 100644 index 00000000000..009bd931e31 --- /dev/null +++ b/sale_order_product_recommendation/__openerp__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Jairo Llopis +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Sale Order Product Recommendation", + "summary": "Recommend products to sell to customer based on history", + "version": "9.0.1.0.0", + "category": "Sales", + "website": "https://www.tecnativa.com/", + "author": "Tecnativa, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "sale", + ], + "data": [ + "wizards/sale_order_recommendation_view.xml", + "views/sale_order_view.xml", + ], +} diff --git a/sale_order_product_recommendation/i18n/es.po b/sale_order_product_recommendation/i18n/es.po new file mode 100644 index 00000000000..0684fd43a26 --- /dev/null +++ b/sale_order_product_recommendation/i18n/es.po @@ -0,0 +1,170 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_product_recommendation +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-09 11:47+0000\n" +"PO-Revision-Date: 2017-10-09 13:50+0200\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.0.3\n" +"Last-Translator: Jairo Llopis \n" +"Language: es_ES\n" + +#. module: sale_order_product_recommendation +#: model:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form +msgid "Accept" +msgstr "Aceptar" + +#. module: sale_order_product_recommendation +#: model:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_months +msgid "Consider these months backwards to generate recommendations." +msgstr "Considerar estos meses atrás para generar las recomendaciones." + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_create_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_create_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_create_date +msgid "Created on" +msgstr "Creado el" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_currency_id +msgid "Currency" +msgstr "Moneda" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_partner_id +msgid "Customer" +msgstr "Cliente" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_display_name +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_id +msgid "ID" +msgstr "ID" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation___last_update +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_write_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_write_uid +msgid "Last Updated by" +msgstr "Última modificación por" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_write_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_months +msgid "Months" +msgstr "Meses" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_amount +msgid "Number of recommendations" +msgstr "Número de recomendaciones" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_price_unit +msgid "Price unit" +msgstr "Precio unitario" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_pricelist_id +msgid "Pricelist" +msgstr "Tarifa" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_pricelist_id +msgid "Pricelist for current sales order." +msgstr "Tarifa del pedido de ventas actual." + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_product_id +msgid "Product" +msgstr "Producto" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_ids +msgid "Products" +msgstr "Productos" + +#. module: sale_order_product_recommendation +#: model:ir.ui.view,arch_db:sale_order_product_recommendation.view_order_form +msgid "Recommended Products" +msgstr "Productos recomendados" + +#. module: sale_order_product_recommendation +#: model:ir.actions.act_window,name:sale_order_product_recommendation.sale_order_recommendation_action +msgid "Recommended Products for this Customer" +msgstr "Productos recomendados para este cliente" + +#. module: sale_order_product_recommendation +#: model:ir.model,name:sale_order_product_recommendation.model_sale_order_recommendation_line +msgid "Recommended product for current sale order" +msgstr "Producto recomendado para el pedido de venta actual" + +#. module: sale_order_product_recommendation +#: model:ir.model,name:sale_order_product_recommendation.model_sale_order_recommendation +msgid "Recommended products for current sale order" +msgstr "Productos recomendados para el pedido de venta actual" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_order_id +msgid "Sale Order" +msgstr "Pedido de venta" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_amount +msgid "The less, the faster they will be found." +msgstr "Cuantos menos, más rápido se encontrarán." + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_times_delivered +msgid "Times delivered" +msgstr "Veces entregado" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_delivered +msgid "Units delivered" +msgstr "Unidades entregadas" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_included +msgid "Units included" +msgstr "Unidades incluidas" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_wizard_id +msgid "Wizard" +msgstr "Asistente" diff --git a/sale_order_product_recommendation/static/description/icon.png b/sale_order_product_recommendation/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/sale_order_product_recommendation/tests/__init__.py b/sale_order_product_recommendation/tests/__init__.py new file mode 100644 index 00000000000..d3cb2c95ae3 --- /dev/null +++ b/sale_order_product_recommendation/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_recommendation diff --git a/sale_order_product_recommendation/tests/test_recommendation.py b/sale_order_product_recommendation/tests/test_recommendation.py new file mode 100644 index 00000000000..80fc3e040b5 --- /dev/null +++ b/sale_order_product_recommendation/tests/test_recommendation.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Jairo Llopis +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.tests.common import SavepointCase + + +class RecommendationCase(SavepointCase): + @classmethod + def setUpClass(cls): + super(RecommendationCase, cls).setUpClass() + cls.camptocamp = cls.env.ref("base.res_partner_12") + cls.product_12 = cls.env.ref('product.product_product_12') + cls.product_46 = cls.env.ref('product.product_product_46') + cls.product_57 = cls.env.ref('product.product_product_57') + # Create old sale orders to have searchable history + cls.env["sale.order"].create({ + "partner_id": cls.camptocamp.id, + "state": "done", + "order_line": [ + (0, 0, { + "product_id": cls.product_12.id, + "name": cls.product_12.name, + "qty_delivered": 25, + }), + (0, 0, { + "product_id": cls.product_46.id, + "name": cls.product_46.name, + "qty_delivered": 50, + }), + (0, 0, { + "product_id": cls.product_57.id, + "name": cls.product_57.name, + "qty_delivered": 100, + }), + ], + }) + cls.env["sale.order"].create({ + "partner_id": cls.camptocamp.id, + "state": "done", + "order_line": [ + (0, 0, { + "product_id": cls.product_46.id, + "name": cls.product_46.name, + "qty_delivered": 50, + }), + ], + }) + # Create a new sale order for the same customer + cls.new_so = cls.env["sale.order"].create({ + "partner_id": cls.camptocamp.id, + }) + + def wizard(self): + """Get a wizard.""" + wizard = self.env["sale.order.recommendation"] \ + .with_context(active_id=self.new_so.id) \ + .create({}) + wizard._generate_recommendations() + return wizard + + def test_recommendations(self): + """Recommendations are OK.""" + self.new_so.order_line = [(0, 0, { + "product_id": self.product_12.id, + "product_uom_qty": 3, + })] + self.new_so.order_line.product_id_change() + wizard = self.wizard() + # Order came in from context + self.assertEqual(wizard.order_id, self.new_so) + # Product 12 is first recommendation because it's in the SO already + self.assertEqual(wizard.line_ids[0].product_id, self.product_12) + self.assertEqual(wizard.line_ids[0].times_delivered, 2) + self.assertEqual(wizard.line_ids[0].units_delivered, 25) + self.assertEqual(wizard.line_ids[0].units_included, 3) + # Product 46 appears second + self.assertEqual(wizard.line_ids[1].product_id, self.product_46) + self.assertEqual(wizard.line_ids[1].times_delivered, 2) + self.assertEqual(wizard.line_ids[1].units_delivered, 100) + self.assertEqual(wizard.line_ids[1].units_included, 0) + # Product 57 appears third + self.assertEqual(wizard.line_ids[2].product_id, self.product_57) + self.assertEqual(wizard.line_ids[2].times_delivered, 1) + self.assertEqual(wizard.line_ids[2].units_delivered, 100) + self.assertEqual(wizard.line_ids[2].units_included, 0) + # Only 1 product if limited as such + wizard.line_amount = 1 + wizard._generate_recommendations() + self.assertEqual(len(wizard.line_ids), 1) + + def test_transfer(self): + """Products get transferred to SO.""" + qty = 10 + wizard = self.wizard() + wizard.line_ids[2].units_included = qty + wizard.action_accept() + self.assertEqual(len(self.new_so.order_line), 1) + self.assertEqual(self.new_so.order_line.product_id, self.product_12) + self.assertEqual(self.new_so.order_line.product_uom_qty, qty) diff --git a/sale_order_product_recommendation/views/sale_order_view.xml b/sale_order_product_recommendation/views/sale_order_view.xml new file mode 100644 index 00000000000..d35386bd92d --- /dev/null +++ b/sale_order_product_recommendation/views/sale_order_view.xml @@ -0,0 +1,22 @@ + + + + + + + Recommended products button + sale.order + + +
+
+
+
+ +
diff --git a/sale_order_product_recommendation/wizards/__init__.py b/sale_order_product_recommendation/wizards/__init__.py new file mode 100644 index 00000000000..991dd729887 --- /dev/null +++ b/sale_order_product_recommendation/wizards/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import sale_order_recommendation diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation.py b/sale_order_product_recommendation/wizards/sale_order_recommendation.py new file mode 100644 index 00000000000..e4912bffe50 --- /dev/null +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Jairo Llopis +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime, timedelta +from openerp import api, fields, models + + +class SaleOrderRecommendation(models.TransientModel): + _name = "sale.order.recommendation" + _description = "Recommended products for current sale order" + + order_id = fields.Many2one( + "sale.order", + "Sale Order", + default=lambda self: self._default_order_id(), + required=True, + readonly=True, + ondelete="cascade", + ) + months = fields.Float( + default=6, + required=True, + help="Consider these months backwards to generate recommendations.", + ) + line_ids = fields.One2many( + "sale.order.recommendation.line", + "wizard_id", + "Products", + ) + line_amount = fields.Integer( + "Number of recommendations", + default=15, + required=True, + help="The less, the faster they will be found.", + ) + + @api.model + def _default_order_id(self): + return self.env.context.get("active_id", False) + + @api.multi + @api.onchange("order_id", "months", "line_amount") + def _generate_recommendations(self): + """Generate lines according to context sale order.""" + start = datetime.now() - timedelta(days=self.months * 30) + start = fields.Datetime.to_string(start) + order_lines = self.order_id.order_line + existing_product_ids = set(order_lines.mapped("product_id").ids) + self.line_ids = False + # Search delivered products in previous months + found_lines = self.env["sale.order.line"].read_group( + [ + ("order_partner_id", "child_of", + self.order_id.partner_id.commercial_partner_id.id), + ("order_id.date_order", ">=", start), + "|", ("qty_delivered", "!=", 0.0), + ("order_id", "=", self.order_id.id), + ], + ["product_id", "qty_delivered"], + ["product_id"], + limit=self.line_amount, + lazy=False, + ) + # Manual ordering that circumvents ORM limitations + found_lines = sorted( + found_lines, + key=lambda res: ( + res["product_id"][0] in existing_product_ids, + res["__count"], + res["qty_delivered"], + ), + reverse=True, + ) + # Add those recommendations too + for line in found_lines: + new_line = self.env["sale.order.recommendation.line"].new({ + "product_id": line["product_id"][0], + "times_delivered": line["__count"], + "units_delivered": line["qty_delivered"], + }) + if new_line.product_id.id in existing_product_ids: + new_line.units_included = ( + order_lines + .filtered(lambda r: r.product_id == new_line.product_id) + .product_uom_qty) + self.line_ids += new_line + + @api.multi + def action_accept(self): + """Propagate recommendations to sale order.""" + so_lines = self.env["sale.order.line"] + existing_products = self.order_id.mapped("order_line.product_id") + for wiz_line in self.line_ids: + # Use preexisting line if any + if wiz_line.product_id <= existing_products: + so_line = self.order_id.order_line.filtered( + lambda r: r.product_id == wiz_line.product_id) + # Merge multiple if needed + if len(so_line) > 1: + so_line[0].product_uom_qty += sum( + so_line[1:].mapped("product_uom_qty")) + so_line[1:].unlink() + # Use a new in-memory line otherwise + else: + so_line = so_lines.new({ + "order_id": self.order_id.id, + }) + # Delete if needed + if not wiz_line.units_included: + so_line.unlink() + continue + # Apply changes + so_line.update({ + "name": wiz_line.product_id.display_name, + "product_id": wiz_line.product_id.id, + "product_uom_qty": wiz_line.units_included, + "product_uom": wiz_line.product_id.uom_id.id, + }) + so_line.product_id_change() + so_line.product_uom_change() + so_lines |= so_line + self.order_id.order_line |= so_lines + + +class SaleOrderRecommendationLine(models.TransientModel): + _name = "sale.order.recommendation.line" + _description = "Recommended product for current sale order" + _order = "id" + + currency_id = fields.Many2one( + related="product_id.currency_id", + readonly=True, + ) + partner_id = fields.Many2one( + related="wizard_id.order_id.partner_id", + readonly=True, + ) + product_id = fields.Many2one( + "product.product", + string="Product", + readonly=True, + ) + price_unit = fields.Monetary( + compute="_compute_price_unit", + ) + pricelist_id = fields.Many2one( + related="wizard_id.order_id.pricelist_id", + readonly=True, + ) + times_delivered = fields.Integer( + readonly=True, + ) + units_delivered = fields.Float( + readonly=True, + ) + units_included = fields.Float() + wizard_id = fields.Many2one( + "sale.order.recommendation", + "Wizard", + ondelete="cascade", + required=True, + readonly=True, + ) + + @api.multi + @api.depends("partner_id", "product_id", "pricelist_id", "units_included") + def _compute_price_unit(self): + for one in self: + one.price_unit = one.product_id.with_context( + partner=one.partner_id.id, + pricelist=one.pricelist_id.id, + quantity=one.units_included, + ).price diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml new file mode 100644 index 00000000000..c8e76b53f78 --- /dev/null +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml @@ -0,0 +1,53 @@ + + + + + + + Recommended Products for this Customer + sale.order.recommendation + +
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + Recommended Products for this Customer + sale.order.recommendation + form + form + new + + +
From b5b3a84284f511802a8f892cc1f3e162ea64ac37 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Fri, 2 Mar 2018 21:06:12 +0100 Subject: [PATCH 02/94] [FIX] sale_order_product_recommendation: Limit after populating results We can't limit results on read_group, because we can't set the same criteria to order results, and thus the N groups populated might be not the top ones looked for. --- .../wizards/sale_order_recommendation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation.py b/sale_order_product_recommendation/wizards/sale_order_recommendation.py index e4912bffe50..02cc46aeb65 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation.py +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation.py @@ -59,7 +59,6 @@ def _generate_recommendations(self): ], ["product_id", "qty_delivered"], ["product_id"], - limit=self.line_amount, lazy=False, ) # Manual ordering that circumvents ORM limitations @@ -73,7 +72,7 @@ def _generate_recommendations(self): reverse=True, ) # Add those recommendations too - for line in found_lines: + for i, line in enumerate(found_lines): new_line = self.env["sale.order.recommendation.line"].new({ "product_id": line["product_id"][0], "times_delivered": line["__count"], @@ -85,6 +84,10 @@ def _generate_recommendations(self): .filtered(lambda r: r.product_id == new_line.product_id) .product_uom_qty) self.line_ids += new_line + # limit number of results. It has to be done here, as we need to + # populate all results first, for being able to select best matches + if (i + 1) == self.line_amount: + break @api.multi def action_accept(self): From 139ed512572c10c482674feedb7ff8054fdb773d Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Sun, 4 Mar 2018 00:42:51 +0100 Subject: [PATCH 03/94] [IMP] sale_order_product_recommendation: Improve performance --- sale_order_product_recommendation/i18n/es.po | 20 +- .../sale_order_product_recommendation.pot | 182 ++++++++++++++++++ .../tests/test_recommendation.py | 3 +- .../wizards/sale_order_recommendation.py | 112 +++++++---- .../sale_order_recommendation_view.xml | 2 + 5 files changed, 275 insertions(+), 44 deletions(-) create mode 100644 sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot diff --git a/sale_order_product_recommendation/i18n/es.po b/sale_order_product_recommendation/i18n/es.po index 0684fd43a26..e031a821c44 100644 --- a/sale_order_product_recommendation/i18n/es.po +++ b/sale_order_product_recommendation/i18n/es.po @@ -8,14 +8,14 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-10-09 11:47+0000\n" "PO-Revision-Date: 2017-10-09 13:50+0200\n" +"Last-Translator: Jairo Llopis \n" "Language-Team: \n" +"Language: es_ES\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.0.3\n" -"Last-Translator: Jairo Llopis \n" -"Language: es_ES\n" #. module: sale_order_product_recommendation #: model:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form @@ -66,6 +66,12 @@ msgstr "Nombre a mostrar" msgid "ID" msgstr "ID" +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_is_modified +#, fuzzy +msgid "Is modified" +msgstr "Última modificación en" + #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation___last_update #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line___last_update @@ -84,6 +90,11 @@ msgstr "Última modificación por" msgid "Last Updated on" msgstr "Última actualización en" +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_last_compute +msgid "Last compute" +msgstr "" + #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_months msgid "Months" @@ -144,6 +155,11 @@ msgstr "Productos recomendados para el pedido de venta actual" msgid "Sale Order" msgstr "Pedido de venta" +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_sale_line_id +msgid "Sale line id" +msgstr "" + #. module: sale_order_product_recommendation #: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_amount msgid "The less, the faster they will be found." diff --git a/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot b/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot new file mode 100644 index 00000000000..db275420ad8 --- /dev/null +++ b/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot @@ -0,0 +1,182 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_product_recommendation +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\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: sale_order_product_recommendation +#: model:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form +msgid "Accept" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form +msgid "Cancel" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_months +msgid "Consider these months backwards to generate recommendations." +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_create_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_create_uid +msgid "Created by" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_create_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_create_date +msgid "Created on" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_currency_id +msgid "Currency" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_partner_id +msgid "Customer" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_display_name +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_display_name +msgid "Display Name" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_id +msgid "ID" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_is_modified +msgid "Is modified" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation___last_update +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line___last_update +msgid "Last Modified on" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_write_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_write_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_write_date +msgid "Last Updated on" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_last_compute +msgid "Last compute" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_months +msgid "Months" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_amount +msgid "Number of recommendations" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_price_unit +msgid "Price unit" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_pricelist_id +msgid "Pricelist" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_pricelist_id +msgid "Pricelist for current sales order." +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_product_id +msgid "Product" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_ids +msgid "Products" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.ui.view,arch_db:sale_order_product_recommendation.view_order_form +msgid "Recommended Products" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.actions.act_window,name:sale_order_product_recommendation.sale_order_recommendation_action +msgid "Recommended Products for this Customer" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model,name:sale_order_product_recommendation.model_sale_order_recommendation_line +msgid "Recommended product for current sale order" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model,name:sale_order_product_recommendation.model_sale_order_recommendation +msgid "Recommended products for current sale order" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_order_id +msgid "Sale Order" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_sale_line_id +msgid "Sale line id" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_amount +msgid "The less, the faster they will be found." +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_times_delivered +msgid "Times delivered" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_delivered +msgid "Units delivered" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_included +msgid "Units included" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_wizard_id +msgid "Wizard" +msgstr "" + diff --git a/sale_order_product_recommendation/tests/test_recommendation.py b/sale_order_product_recommendation/tests/test_recommendation.py index 80fc3e040b5..53bdcbe12c3 100644 --- a/sale_order_product_recommendation/tests/test_recommendation.py +++ b/sale_order_product_recommendation/tests/test_recommendation.py @@ -87,13 +87,14 @@ def test_recommendations(self): # Only 1 product if limited as such wizard.line_amount = 1 wizard._generate_recommendations() - self.assertEqual(len(wizard.line_ids), 1) + self.assertEqual(len(wizard.line_ids), 2) def test_transfer(self): """Products get transferred to SO.""" qty = 10 wizard = self.wizard() wizard.line_ids[2].units_included = qty + wizard.line_ids[2]._onchange_units_included() wizard.action_accept() self.assertEqual(len(self.new_so.order_line), 1) self.assertEqual(self.new_so.order_line.product_id, self.product_12) diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation.py b/sale_order_product_recommendation/wizards/sale_order_recommendation.py index 02cc46aeb65..dfd5fbd5796 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation.py +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Copyright 2017 Jairo Llopis +# Copyright 2018 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from datetime import datetime, timedelta @@ -34,6 +35,7 @@ class SaleOrderRecommendation(models.TransientModel): required=True, help="The less, the faster they will be found.", ) + last_compute = fields.Char() @api.model def _default_order_id(self): @@ -43,84 +45,104 @@ def _default_order_id(self): @api.onchange("order_id", "months", "line_amount") def _generate_recommendations(self): """Generate lines according to context sale order.""" + last_compute = '{}-{}-{}'.format( + self.id, self.months, self.line_amount) + # Avoid execute onchange as times as fields in api.onchange + # ORM must control this? + if self.last_compute == last_compute: + return + self.last_compute = last_compute start = datetime.now() - timedelta(days=self.months * 30) start = fields.Datetime.to_string(start) - order_lines = self.order_id.order_line - existing_product_ids = set(order_lines.mapped("product_id").ids) self.line_ids = False # Search delivered products in previous months + sales = self.env["sale.order"].search([ + ("partner_id", "child_of", + self.order_id.partner_id.commercial_partner_id.id), + ("date_order", ">=", start), + ]) found_lines = self.env["sale.order.line"].read_group( [ - ("order_partner_id", "child_of", - self.order_id.partner_id.commercial_partner_id.id), - ("order_id.date_order", ">=", start), - "|", ("qty_delivered", "!=", 0.0), + ("order_id", "in", sales.ids), + '|', ("qty_delivered", "!=", 0.0), ("order_id", "=", self.order_id.id), ], ["product_id", "qty_delivered"], ["product_id"], - lazy=False, ) # Manual ordering that circumvents ORM limitations found_lines = sorted( found_lines, key=lambda res: ( - res["product_id"][0] in existing_product_ids, - res["__count"], + res["product_id_count"], res["qty_delivered"], ), reverse=True, ) + found_dict = {l["product_id"][0]: l for l in found_lines} + RecomendationLine = self.env["sale.order.recommendation.line"] + existing_product_ids = [] + # Add products from sale order lines + for line in self.order_id.order_line: + found_line = found_dict[line.product_id.id] + new_line = RecomendationLine.new({ + "product_id": line.product_id.id, + "times_delivered": found_line["product_id_count"], + "units_delivered": found_line["qty_delivered"], + "units_included": line.product_uom_qty, + "sale_line_id": line.id, + }) + self.line_ids += new_line + existing_product_ids.append(line.product_id.id) # Add those recommendations too - for i, line in enumerate(found_lines): - new_line = self.env["sale.order.recommendation.line"].new({ + i = 0 + for line in found_lines: + if line["product_id"][0] in existing_product_ids: + continue + new_line = RecomendationLine.new({ "product_id": line["product_id"][0], - "times_delivered": line["__count"], + "times_delivered": line["product_id_count"], "units_delivered": line["qty_delivered"], }) - if new_line.product_id.id in existing_product_ids: - new_line.units_included = ( - order_lines - .filtered(lambda r: r.product_id == new_line.product_id) - .product_uom_qty) self.line_ids += new_line # limit number of results. It has to be done here, as we need to # populate all results first, for being able to select best matches - if (i + 1) == self.line_amount: + i += 1 + if i == self.line_amount: break + @api.model + def create(self, vals): + if 'line_ids' in vals: + vals['line_ids'] = [ + line for line in vals['line_ids'] if line[2]["is_modified"]] + return super(SaleOrderRecommendation, self).create(vals) + @api.multi def action_accept(self): """Propagate recommendations to sale order.""" so_lines = self.env["sale.order.line"] - existing_products = self.order_id.mapped("order_line.product_id") - for wiz_line in self.line_ids: + sequence = max(self.order_id.mapped('order_line.sequence') or [0]) + for wiz_line in self.line_ids.filtered('is_modified'): # Use preexisting line if any - if wiz_line.product_id <= existing_products: - so_line = self.order_id.order_line.filtered( - lambda r: r.product_id == wiz_line.product_id) - # Merge multiple if needed - if len(so_line) > 1: - so_line[0].product_uom_qty += sum( - so_line[1:].mapped("product_uom_qty")) - so_line[1:].unlink() - # Use a new in-memory line otherwise - else: - so_line = so_lines.new({ - "order_id": self.order_id.id, - }) - # Delete if needed - if not wiz_line.units_included: - so_line.unlink() + if wiz_line.sale_line_id: + if wiz_line.units_included: + wiz_line.sale_line_id.update({ + "product_uom_qty": wiz_line.units_included, + }) + wiz_line.sale_line_id.product_uom_change() + else: + wiz_line.sale_line_id.unlink() continue - # Apply changes - so_line.update({ - "name": wiz_line.product_id.display_name, + sequence += 1 + # Use a new in-memory line otherwise + so_line = so_lines.new({ + "order_id": self.order_id.id, "product_id": wiz_line.product_id.id, - "product_uom_qty": wiz_line.units_included, - "product_uom": wiz_line.product_id.uom_id.id, + "sequence": sequence, }) so_line.product_id_change() + so_line.product_uom_qty = wiz_line.units_included so_line.product_uom_change() so_lines |= so_line self.order_id.order_line |= so_lines @@ -165,6 +187,10 @@ class SaleOrderRecommendationLine(models.TransientModel): required=True, readonly=True, ) + sale_line_id = fields.Many2one( + comodel_name="sale.order.line", + ) + is_modified = fields.Boolean() @api.multi @api.depends("partner_id", "product_id", "pricelist_id", "units_included") @@ -175,3 +201,7 @@ def _compute_price_unit(self): pricelist=one.pricelist_id.id, quantity=one.units_included, ).price + + @api.onchange("units_included") + def _onchange_units_included(self): + self.is_modified = bool(self.sale_line_id or self.units_included) diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml index c8e76b53f78..6fed13a0c2d 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml @@ -20,6 +20,8 @@ + + From c14e1fbe9d25e2b83bedf859f5eda2873dbcef2f Mon Sep 17 00:00:00 2001 From: David Date: Thu, 12 Jul 2018 16:32:33 +0200 Subject: [PATCH 04/94] [MIG] sale_order_product_recommendation: Migration to 11.0 --- sale_order_product_recommendation/README.rst | 68 +++++++++++++------ sale_order_product_recommendation/__init__.py | 1 - .../{__openerp__.py => __manifest__.py} | 5 +- .../readme/CONTRIBUTORS.rst | 4 ++ .../readme/DESCRIPTION.rst | 5 ++ .../readme/USAGE.rst | 7 ++ .../tests/__init__.py | 2 +- .../tests/test_recommendation.py | 23 +++---- .../wizards/__init__.py | 1 - .../wizards/sale_order_recommendation.py | 3 +- 10 files changed, 77 insertions(+), 42 deletions(-) rename sale_order_product_recommendation/{__openerp__.py => __manifest__.py} (85%) create mode 100644 sale_order_product_recommendation/readme/CONTRIBUTORS.rst create mode 100644 sale_order_product_recommendation/readme/DESCRIPTION.rst create mode 100644 sale_order_product_recommendation/readme/USAGE.rst diff --git a/sale_order_product_recommendation/README.rst b/sale_order_product_recommendation/README.rst index 90944302469..f9ed1bb7114 100644 --- a/sale_order_product_recommendation/README.rst +++ b/sale_order_product_recommendation/README.rst @@ -1,17 +1,41 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl - :alt: License: AGPL-3 - ================================= Sale Order Product Recommendation ================================= +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/sale-workflow/tree/11.0/sale_order_product_recommendation + :alt: OCA/sale-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-workflow-11-0/sale-workflow-11-0-sale_order_product_recommendation + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/167/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + This module adds a recommended products wizard to current sale order. It is based on recent delivered products, and allows the salesman to quickly know the most sold products for current customer, which results in an easy to use hint to improve sale. +**Table of contents** + +.. contents:: + :local: + Usage ===== @@ -23,45 +47,45 @@ To use this module, you need to: #. Add products into the opened wizard. #. Press *Accept*. -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/167/9.0 - 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. +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= -Images ------- +Authors +~~~~~~~ -* Odoo Community Association: `Icon `_. +* Tecnativa Contributors ------------- +~~~~~~~~~~~~ * `Tecnativa `_: + * Jairo Llopis + * David Vidal -Do not contact contributors directly about support or help with technical issues. +Maintainers +~~~~~~~~~~~ -Maintainer ----------- +This module is maintained by the OCA. .. 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. +This module is part of the `OCA/sale-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_order_product_recommendation/__init__.py b/sale_order_product_recommendation/__init__.py index 0796ed1dec6..fbadf7a68ce 100644 --- a/sale_order_product_recommendation/__init__.py +++ b/sale_order_product_recommendation/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import wizards diff --git a/sale_order_product_recommendation/__openerp__.py b/sale_order_product_recommendation/__manifest__.py similarity index 85% rename from sale_order_product_recommendation/__openerp__.py rename to sale_order_product_recommendation/__manifest__.py index 009bd931e31..782e816cde7 100644 --- a/sale_order_product_recommendation/__openerp__.py +++ b/sale_order_product_recommendation/__manifest__.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Jairo Llopis # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { "name": "Sale Order Product Recommendation", "summary": "Recommend products to sell to customer based on history", - "version": "9.0.1.0.0", + "version": "11.0.1.0.0", "category": "Sales", - "website": "https://www.tecnativa.com/", + "website": "https://github.com/OCA/sale-workflow", "author": "Tecnativa, Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, diff --git a/sale_order_product_recommendation/readme/CONTRIBUTORS.rst b/sale_order_product_recommendation/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..454d20da738 --- /dev/null +++ b/sale_order_product_recommendation/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Tecnativa `_: + + * Jairo Llopis + * David Vidal diff --git a/sale_order_product_recommendation/readme/DESCRIPTION.rst b/sale_order_product_recommendation/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..90f24468977 --- /dev/null +++ b/sale_order_product_recommendation/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module adds a recommended products wizard to current sale order. + +It is based on recent delivered products, and allows the salesman to quickly +know the most sold products for current customer, which results in an easy to +use hint to improve sale. diff --git a/sale_order_product_recommendation/readme/USAGE.rst b/sale_order_product_recommendation/readme/USAGE.rst new file mode 100644 index 00000000000..93174b16646 --- /dev/null +++ b/sale_order_product_recommendation/readme/USAGE.rst @@ -0,0 +1,7 @@ +To use this module, you need to: + +#. Create a new sale order. +#. Assign its customer. +#. Press *Recommended Products* button. +#. Add products into the opened wizard. +#. Press *Accept*. diff --git a/sale_order_product_recommendation/tests/__init__.py b/sale_order_product_recommendation/tests/__init__.py index d3cb2c95ae3..eda1ad446d6 100644 --- a/sale_order_product_recommendation/tests/__init__.py +++ b/sale_order_product_recommendation/tests/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import test_recommendation diff --git a/sale_order_product_recommendation/tests/test_recommendation.py b/sale_order_product_recommendation/tests/test_recommendation.py index 53bdcbe12c3..dfbd17ff3d4 100644 --- a/sale_order_product_recommendation/tests/test_recommendation.py +++ b/sale_order_product_recommendation/tests/test_recommendation.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Jairo Llopis # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.tests.common import SavepointCase +from odoo.tests.common import SavepointCase class RecommendationCase(SavepointCase): @@ -11,8 +10,8 @@ def setUpClass(cls): super(RecommendationCase, cls).setUpClass() cls.camptocamp = cls.env.ref("base.res_partner_12") cls.product_12 = cls.env.ref('product.product_product_12') - cls.product_46 = cls.env.ref('product.product_product_46') - cls.product_57 = cls.env.ref('product.product_product_57') + cls.product_10 = cls.env.ref('product.product_product_10') + cls.product_20 = cls.env.ref('product.product_product_20') # Create old sale orders to have searchable history cls.env["sale.order"].create({ "partner_id": cls.camptocamp.id, @@ -24,13 +23,13 @@ def setUpClass(cls): "qty_delivered": 25, }), (0, 0, { - "product_id": cls.product_46.id, - "name": cls.product_46.name, + "product_id": cls.product_10.id, + "name": cls.product_10.name, "qty_delivered": 50, }), (0, 0, { - "product_id": cls.product_57.id, - "name": cls.product_57.name, + "product_id": cls.product_20.id, + "name": cls.product_20.name, "qty_delivered": 100, }), ], @@ -40,8 +39,8 @@ def setUpClass(cls): "state": "done", "order_line": [ (0, 0, { - "product_id": cls.product_46.id, - "name": cls.product_46.name, + "product_id": cls.product_10.id, + "name": cls.product_10.name, "qty_delivered": 50, }), ], @@ -75,12 +74,12 @@ def test_recommendations(self): self.assertEqual(wizard.line_ids[0].units_delivered, 25) self.assertEqual(wizard.line_ids[0].units_included, 3) # Product 46 appears second - self.assertEqual(wizard.line_ids[1].product_id, self.product_46) + self.assertEqual(wizard.line_ids[1].product_id, self.product_10) self.assertEqual(wizard.line_ids[1].times_delivered, 2) self.assertEqual(wizard.line_ids[1].units_delivered, 100) self.assertEqual(wizard.line_ids[1].units_included, 0) # Product 57 appears third - self.assertEqual(wizard.line_ids[2].product_id, self.product_57) + self.assertEqual(wizard.line_ids[2].product_id, self.product_20) self.assertEqual(wizard.line_ids[2].times_delivered, 1) self.assertEqual(wizard.line_ids[2].units_delivered, 100) self.assertEqual(wizard.line_ids[2].units_included, 0) diff --git a/sale_order_product_recommendation/wizards/__init__.py b/sale_order_product_recommendation/wizards/__init__.py index 991dd729887..a4020cee264 100644 --- a/sale_order_product_recommendation/wizards/__init__.py +++ b/sale_order_product_recommendation/wizards/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import sale_order_recommendation diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation.py b/sale_order_product_recommendation/wizards/sale_order_recommendation.py index dfd5fbd5796..9a96a416828 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation.py +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Jairo Llopis # Copyright 2018 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from datetime import datetime, timedelta -from openerp import api, fields, models +from odoo import api, fields, models class SaleOrderRecommendation(models.TransientModel): From dcb6de3223d629212faf35ffe5c426cf0c9a3029 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Tue, 11 Sep 2018 07:38:02 +0000 Subject: [PATCH 05/94] [UPD] Update sale_order_product_recommendation.pot --- .../sale_order_product_recommendation.pot | 30 +- .../static/description/index.html | 438 ++++++++++++++++++ 2 files changed, 453 insertions(+), 15 deletions(-) create mode 100644 sale_order_product_recommendation/static/description/index.html diff --git a/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot b/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot index db275420ad8..3638b4b4652 100644 --- a/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot +++ b/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 9.0c\n" +"Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: <>\n" "Language-Team: \n" @@ -64,7 +64,12 @@ msgstr "" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_is_modified -msgid "Is modified" +msgid "Is Modified" +msgstr "" + +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_last_compute +msgid "Last Compute" msgstr "" #. module: sale_order_product_recommendation @@ -85,11 +90,6 @@ msgstr "" msgid "Last Updated on" msgstr "" -#. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_last_compute -msgid "Last compute" -msgstr "" - #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_months msgid "Months" @@ -102,7 +102,7 @@ msgstr "" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_price_unit -msgid "Price unit" +msgid "Price Unit" msgstr "" #. module: sale_order_product_recommendation @@ -146,13 +146,13 @@ msgid "Recommended products for current sale order" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_order_id -msgid "Sale Order" +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_sale_line_id +msgid "Sale Line" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_sale_line_id -msgid "Sale line id" +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_order_id +msgid "Sale Order" msgstr "" #. module: sale_order_product_recommendation @@ -162,17 +162,17 @@ msgstr "" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_times_delivered -msgid "Times delivered" +msgid "Times Delivered" msgstr "" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_delivered -msgid "Units delivered" +msgid "Units Delivered" msgstr "" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_included -msgid "Units included" +msgid "Units Included" msgstr "" #. module: sale_order_product_recommendation diff --git a/sale_order_product_recommendation/static/description/index.html b/sale_order_product_recommendation/static/description/index.html new file mode 100644 index 00000000000..630d6788a40 --- /dev/null +++ b/sale_order_product_recommendation/static/description/index.html @@ -0,0 +1,438 @@ + + + + + + +Sale Order Product Recommendation + + + +
+

Sale Order Product Recommendation

+ + +

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

+

This module adds a recommended products wizard to current sale order.

+

It is based on recent delivered products, and allows the salesman to quickly +know the most sold products for current customer, which results in an easy to +use hint to improve sale.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  1. Create a new sale order.
  2. +
  3. Assign its customer.
  4. +
  5. Press Recommended Products button.
  6. +
  7. Add products into the opened wizard.
  8. +
  9. Press Accept.
  10. +
+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/sale-workflow project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + From 80f8fca782cb85e7177ac4858642c8483cfe3725 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 20 Sep 2018 13:02:28 +0200 Subject: [PATCH 06/94] [FIX] sale_order_product_recommendation: wizard only worked with order line products --- .../wizards/sale_order_recommendation.py | 1 - .../wizards/sale_order_recommendation_view.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation.py b/sale_order_product_recommendation/wizards/sale_order_recommendation.py index 9a96a416828..b3ebff0671a 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation.py +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation.py @@ -163,7 +163,6 @@ class SaleOrderRecommendationLine(models.TransientModel): product_id = fields.Many2one( "product.product", string="Product", - readonly=True, ) price_unit = fields.Monetary( compute="_compute_price_unit", diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml index 6fed13a0c2d..eca6177e191 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml @@ -22,7 +22,7 @@ - + From 56160c2827607626b1b15f84f4487f0a5ce48c92 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 20 Sep 2018 13:03:12 +0200 Subject: [PATCH 07/94] [IMP] sale_order_product_recommendation: button visibility - When a SO is confirmed the button is still visible so the wizard can be used to add or remove lines and products. The same restrictions that exist editing the order manually apply to the wizard. --- .../__manifest__.py | 2 +- .../tests/test_recommendation.py | 36 +++++++++++++++++++ .../views/sale_order_view.xml | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/sale_order_product_recommendation/__manifest__.py b/sale_order_product_recommendation/__manifest__.py index 782e816cde7..4de0b8295f6 100644 --- a/sale_order_product_recommendation/__manifest__.py +++ b/sale_order_product_recommendation/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Sale Order Product Recommendation", "summary": "Recommend products to sell to customer based on history", - "version": "11.0.1.0.0", + "version": "11.0.1.0.1", "category": "Sales", "website": "https://github.com/OCA/sale-workflow", "author": "Tecnativa, Odoo Community Association (OCA)", diff --git a/sale_order_product_recommendation/tests/test_recommendation.py b/sale_order_product_recommendation/tests/test_recommendation.py index dfbd17ff3d4..49c0cda1388 100644 --- a/sale_order_product_recommendation/tests/test_recommendation.py +++ b/sale_order_product_recommendation/tests/test_recommendation.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.tests.common import SavepointCase +from odoo.exceptions import UserError class RecommendationCase(SavepointCase): @@ -98,3 +99,38 @@ def test_transfer(self): self.assertEqual(len(self.new_so.order_line), 1) self.assertEqual(self.new_so.order_line.product_id, self.product_12) self.assertEqual(self.new_so.order_line.product_uom_qty, qty) + # No we confirm the SO + self.new_so.action_confirm() + wizard = self.wizard() + wiz_line = wizard.line_ids.filtered( + lambda x: x.product_id == self.product_12) + wiz_line.units_included = 0 + wiz_line._onchange_units_included() + # The confirmed line can't be deleted + with self.assertRaises(UserError): + wizard.action_accept() + # Deliver items and invoice the order + self.new_so.order_line.qty_delivered = qty + adv_wiz = self.env['sale.advance.payment.inv'].with_context( + active_ids=[self.new_so.id]).create({ + 'advance_payment_method': 'all', + }) + adv_wiz.with_context(open_invoices=True).create_invoices() + self.new_so.invoice_ids.action_invoice_open() + # Open the wizard and add more product qty + wizard = self.wizard() + wiz_line = wizard.line_ids.filtered( + lambda x: x.product_id == self.product_12) + wiz_line.units_included = qty + 2 + wiz_line._onchange_units_included() + wizard.action_accept() + # Deliver extra qty and make a new invoice + self.new_so.order_line.qty_delivered = qty + 2 + adv_wiz = self.env['sale.advance.payment.inv'].with_context( + active_ids=[self.new_so.id]).create({ + 'advance_payment_method': 'all', + }) + adv_wiz.with_context(open_invoices=True).create_invoices() + self.assertEqual(2, len(self.new_so.invoice_ids)) + self.assertEqual(2, + self.new_so.invoice_ids[:1].invoice_line_ids.quantity) diff --git a/sale_order_product_recommendation/views/sale_order_view.xml b/sale_order_product_recommendation/views/sale_order_view.xml index d35386bd92d..c180bd92ef2 100644 --- a/sale_order_product_recommendation/views/sale_order_view.xml +++ b/sale_order_product_recommendation/views/sale_order_view.xml @@ -12,7 +12,7 @@
From b7dddb7224170a0a07a5c4d7ea024630e331e8c2 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Thu, 25 Oct 2018 14:26:30 +0200 Subject: [PATCH 08/94] [FIX] sale_order_product_recommendation: Save product_id value Using force_save option in views --- sale_order_product_recommendation/i18n/es.po | 35 +++++++++++-------- .../wizards/sale_order_recommendation.py | 7 ---- .../sale_order_recommendation_view.xml | 2 +- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/sale_order_product_recommendation/i18n/es.po b/sale_order_product_recommendation/i18n/es.po index e031a821c44..a66aa5f5685 100644 --- a/sale_order_product_recommendation/i18n/es.po +++ b/sale_order_product_recommendation/i18n/es.po @@ -69,9 +69,14 @@ msgstr "ID" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_is_modified #, fuzzy -msgid "Is modified" +msgid "Is Modified" msgstr "Última modificación en" +#. module: sale_order_product_recommendation +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_last_compute +msgid "Last Compute" +msgstr "" + #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation___last_update #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line___last_update @@ -90,11 +95,6 @@ msgstr "Última modificación por" msgid "Last Updated on" msgstr "Última actualización en" -#. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_last_compute -msgid "Last compute" -msgstr "" - #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_months msgid "Months" @@ -107,7 +107,8 @@ msgstr "Número de recomendaciones" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_price_unit -msgid "Price unit" +#, fuzzy +msgid "Price Unit" msgstr "Precio unitario" #. module: sale_order_product_recommendation @@ -151,14 +152,15 @@ msgid "Recommended products for current sale order" msgstr "Productos recomendados para el pedido de venta actual" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_order_id -msgid "Sale Order" +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_sale_line_id +#, fuzzy +msgid "Sale Line" msgstr "Pedido de venta" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_sale_line_id -msgid "Sale line id" -msgstr "" +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_order_id +msgid "Sale Order" +msgstr "Pedido de venta" #. module: sale_order_product_recommendation #: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_amount @@ -167,17 +169,20 @@ msgstr "Cuantos menos, más rápido se encontrarán." #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_times_delivered -msgid "Times delivered" +#, fuzzy +msgid "Times Delivered" msgstr "Veces entregado" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_delivered -msgid "Units delivered" +#, fuzzy +msgid "Units Delivered" msgstr "Unidades entregadas" #. module: sale_order_product_recommendation #: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_included -msgid "Units included" +#, fuzzy +msgid "Units Included" msgstr "Unidades incluidas" #. module: sale_order_product_recommendation diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation.py b/sale_order_product_recommendation/wizards/sale_order_recommendation.py index b3ebff0671a..209db0b237c 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation.py +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation.py @@ -110,13 +110,6 @@ def _generate_recommendations(self): if i == self.line_amount: break - @api.model - def create(self, vals): - if 'line_ids' in vals: - vals['line_ids'] = [ - line for line in vals['line_ids'] if line[2]["is_modified"]] - return super(SaleOrderRecommendation, self).create(vals) - @api.multi def action_accept(self): """Propagate recommendations to sale order.""" diff --git a/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml index eca6177e191..aa7cee1bcef 100644 --- a/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml +++ b/sale_order_product_recommendation/wizards/sale_order_recommendation_view.xml @@ -22,7 +22,7 @@ - + From 4b9258990c347bc1f038c66156397da6a4e84212 Mon Sep 17 00:00:00 2001 From: david Date: Wed, 20 Nov 2019 18:21:06 +0100 Subject: [PATCH 09/94] [MIG] sale_order_product_recommendation: Migration to 12.0 --- sale_order_product_recommendation/README.rst | 10 +- .../__manifest__.py | 2 +- .../sale_order_product_recommendation.pot | 79 ++++++++------- .../static/description/index.html | 8 +- .../tests/test_recommendation.py | 99 ++++++++++++------- .../wizards/sale_order_recommendation.py | 13 +-- 6 files changed, 119 insertions(+), 92 deletions(-) diff --git a/sale_order_product_recommendation/README.rst b/sale_order_product_recommendation/README.rst index f9ed1bb7114..e6f541b9d42 100644 --- a/sale_order_product_recommendation/README.rst +++ b/sale_order_product_recommendation/README.rst @@ -14,13 +14,13 @@ Sale Order Product Recommendation :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github - :target: https://github.com/OCA/sale-workflow/tree/11.0/sale_order_product_recommendation + :target: https://github.com/OCA/sale-workflow/tree/12.0/sale_order_product_recommendation :alt: OCA/sale-workflow .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/sale-workflow-11-0/sale-workflow-11-0-sale_order_product_recommendation + :target: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_order_product_recommendation :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/167/11.0 + :target: https://runbot.odoo-community.org/runbot/167/12.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -53,7 +53,7 @@ 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 smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -86,6 +86,6 @@ 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. -This module is part of the `OCA/sale-workflow `_ project on GitHub. +This module is part of the `OCA/sale-workflow `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_order_product_recommendation/__manifest__.py b/sale_order_product_recommendation/__manifest__.py index 4de0b8295f6..7bed0bf7e48 100644 --- a/sale_order_product_recommendation/__manifest__.py +++ b/sale_order_product_recommendation/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Sale Order Product Recommendation", "summary": "Recommend products to sell to customer based on history", - "version": "11.0.1.0.1", + "version": "12.0.1.0.0", "category": "Sales", "website": "https://github.com/OCA/sale-workflow", "author": "Tecnativa, Odoo Community Association (OCA)", diff --git a/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot b/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot index 3638b4b4652..2d05039bc92 100644 --- a/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot +++ b/sale_order_product_recommendation/i18n/sale_order_product_recommendation.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: <>\n" "Language-Team: \n" @@ -14,119 +14,119 @@ msgstr "" "Plural-Forms: \n" #. module: sale_order_product_recommendation -#: model:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form +#: model_terms:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form msgid "Accept" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form +#: model_terms:ir.ui.view,arch_db:sale_order_product_recommendation.sale_order_recommendation_view_form msgid "Cancel" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_months +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation__months msgid "Consider these months backwards to generate recommendations." msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_create_uid -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_create_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__create_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__create_uid msgid "Created by" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_create_date -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_create_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__create_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__create_date msgid "Created on" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_currency_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__currency_id msgid "Currency" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_partner_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__partner_id msgid "Customer" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_display_name -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_display_name +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__display_name +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__display_name msgid "Display Name" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_id -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__id msgid "ID" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_is_modified +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__is_modified msgid "Is Modified" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_last_compute +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__last_compute msgid "Last Compute" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation___last_update -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line___last_update +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation____last_update +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line____last_update msgid "Last Modified on" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_write_uid -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_write_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__write_uid +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__write_uid msgid "Last Updated by" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_write_date -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_write_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__write_date +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__write_date msgid "Last Updated on" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_months +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__months msgid "Months" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_amount +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__line_amount msgid "Number of recommendations" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_price_unit +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__price_unit msgid "Price Unit" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_pricelist_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__pricelist_id msgid "Pricelist" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_pricelist_id +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line__pricelist_id msgid "Pricelist for current sales order." msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_product_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__product_id msgid "Product" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_ids +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__line_ids msgid "Products" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.ui.view,arch_db:sale_order_product_recommendation.view_order_form +#: model_terms:ir.ui.view,arch_db:sale_order_product_recommendation.view_order_form msgid "Recommended Products" msgstr "" @@ -146,37 +146,42 @@ msgid "Recommended products for current sale order" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_sale_line_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__sale_line_id msgid "Sale Line" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_order_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation__order_id msgid "Sale Order" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line_amount +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation__line_amount msgid "The less, the faster they will be found." msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_times_delivered +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__times_delivered msgid "Times Delivered" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_delivered +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__units_delivered msgid "Units Delivered" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_units_included +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__units_included msgid "Units Included" msgstr "" #. module: sale_order_product_recommendation -#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line_wizard_id +#: model:ir.model.fields,field_description:sale_order_product_recommendation.field_sale_order_recommendation_line__wizard_id msgid "Wizard" msgstr "" +#. module: sale_order_product_recommendation +#: model:ir.model.fields,help:sale_order_product_recommendation.field_sale_order_recommendation_line__partner_id +msgid "You can find a customer by its Name, TIN, Email or Internal Reference." +msgstr "" + diff --git a/sale_order_product_recommendation/static/description/index.html b/sale_order_product_recommendation/static/description/index.html index 630d6788a40..d3da3987d65 100644 --- a/sale_order_product_recommendation/static/description/index.html +++ b/sale_order_product_recommendation/static/description/index.html @@ -3,7 +3,7 @@ - + Sale Order Product Recommendation