Rodney Elpidio Enciso Arias преди 6 години
ревизия
477523863b

+ 35 - 0
README.rst

@@ -0,0 +1,35 @@
+.. 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
+
+====================================
+Detect and fix easily EAN duplicates
+====================================
+
+Functionality
+-------------
+
+* Add a view to detect and fix EAN duplicates of products into a company
+
+* Remove in the copy fields of product product the field ean
+
+* Add a constrains that prevent user to create two products with the
+  same ean13 into the same company
+
+.. figure:: /pos_ean_duplicates/static/description/product_barcode_constrains.png
+   :width: 800 px
+
+Credits
+=======
+
+Contributors
+------------
+
+* Sylvain LE GAL (https://www.twitter.com/legalsylvain)
+
+Funders
+-------
+
+The development of this module has been financially supported by:
+
+* GRAP, Groupement Régional Alimentaire de Proximité (http://www.grap.coop)

+ 2 - 0
__init__.py

@@ -0,0 +1,2 @@
+# coding: utf-8
+from . import models

BIN
__init__.pyc


+ 30 - 0
__openerp__.py

@@ -0,0 +1,30 @@
+# coding: utf-8
+# Copyright (C) 2014-Today GRAP (http://www.grap.coop)
+# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+
+{
+    'name': 'Product - EAN Duplicates',
+    'summary': 'Detect and fix easily EAN duplicates',
+    'version': '8.0.1.0.0',
+    'category': 'product',
+    'author': 'GRAP',
+    'website': 'http://www.grap.coop',
+    'license': 'AGPL-3',
+    'depends': [
+        'product',
+    ],
+    'data': [
+        'views/view_product_product.xml',
+        'views/action.xml',
+        'views/menu.xml',
+    ],
+    'demo': [
+        'demo/res_groups.xml',
+    ],
+    'images': [
+        'static/description/product_barcode_constrains.png'
+    ],
+    'installable': True,
+}

+ 15 - 0
demo/res_groups.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2015 - Today: GRAP (http://www.grap.coop)
+@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+-->
+<openerp><data>
+
+
+    <record id="base.group_sale_manager" model="res.groups">
+        <field name="users" eval="[(4, ref('base.user_root'))]"/>
+    </record>
+
+
+</data></openerp>

+ 46 - 0
i18n/fr.po

@@ -0,0 +1,46 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* product_ean_duplicates
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-01-30 09:56+0000\n"
+"PO-Revision-Date: 2015-01-30 09:56+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: product_ean_duplicates
+#: view:product.product:0
+msgid "EAN Duplicates"
+msgstr "Doublons EAN"
+
+#. module: product_ean_duplicates
+#: field:product.product,ean_duplicates_qty:0
+msgid "EAN Duplicates Quantity"
+msgstr "Nombre de Doublons EAN"
+
+#. module: product_ean_duplicates
+#: field:product.product,ean_duplicates_exist:0
+msgid "Has EAN Duplicates"
+msgstr "Possède des doublons EAN"
+
+#. module: product_ean_duplicates
+#: code:_description:0
+#: model:ir.model,name:product_ean_duplicates.model_product_product
+#, python-format
+msgid "Product"
+msgstr "Article"
+
+#. module: product_ean_duplicates
+#: model:ir.actions.act_window,name:product_ean_duplicates.action_product_ean_duplicates
+#: model:ir.ui.menu,name:product_ean_duplicates.menu_product_ean_duplicates
+#: view:product.product:0
+msgid "Products (EAN Duplicates)"
+msgstr "Articles (Doublons EAN)"
+

+ 2 - 0
models/__init__.py

@@ -0,0 +1,2 @@
+# coding: utf-8
+from . import product_product

BIN
models/__init__.pyc


+ 87 - 0
models/product_product.py

@@ -0,0 +1,87 @@
+# coding: utf-8
+# Copyright (C) 2014 - Today: GRAP (http://www.grap.coop)
+# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from openerp import _, api, fields, models
+from openerp.exceptions import ValidationError
+from openerp.exceptions import Warning as UserError
+
+
+class ProductProduct(models.Model):
+    _inherit = 'product.product'
+
+    # Column Section
+    ean13 = fields.Char(copy=False)
+
+    ean_duplicates_exist = fields.Boolean(
+        compute='_compute_ean_duplicates',
+        string='Has EAN Duplicates', multi='ean_duplicates',
+        search='_search_ean_duplicates_exist')
+
+    ean_duplicates_qty = fields.Integer(
+        compute='_compute_ean_duplicates',
+        string='EAN Duplicates Quantity', multi='ean_duplicates')
+
+    # Compute Section
+    @api.multi
+    def _compute_ean_duplicates(self):
+        res = self._get_ean_duplicates()
+        for product in self:
+            if product.id in res:
+                product.ean_duplicates_qty = res[product.id]
+                product.ean_duplicates_exist = True
+
+    # Search Section
+    def _search_ean_duplicates_exist(self, operator, operand):
+        products = self.search([])
+        res = products._get_ean_duplicates()
+        if operator == '=' and operand is True:
+            product_ids = res.keys()
+        elif operator == '=' and operand is False:
+            product_ids = list(set(products.ids) - set(res.keys()))
+        else:
+            raise UserError(_(
+                "Operator '%s' not implemented.") % (operator))
+        return [('id', 'in', product_ids)]
+
+    # Constrains Section
+    @api.constrains('company_id', 'ean13')
+    def _check_ean13_company(self):
+        for product in self.filtered(lambda x: x.ean13):
+            duplicates = self.with_context(active_test=True).search([
+                ('company_id', '=', product.company_id.id),
+                ('ean13', '=', product.ean13),
+                ('id', '!=', product.id)])
+            if duplicates:
+                raise ValidationError(_(
+                    "You can not set the ean13 '%s' for the product %s"
+                    " because you have other products with the same"
+                    " ean13 :\n - %s") % (
+                        product.ean13, product.name,
+                        '- %s\n'.join([x.name for x in duplicates])))
+
+    # Private Section
+    @api.multi
+    def _get_ean_duplicates(self):
+        self._cr.execute("""
+            SELECT
+                pp1.id,
+                count(*) as qty
+            FROM product_product pp1
+            INNER JOIN product_template pt1
+                ON pt1.id = pp1.product_tmpl_id
+            INNER JOIN product_product pp2
+                ON pp1.ean13 = pp2.ean13
+                AND pp1.id != pp2.id
+                AND pp2.active = True
+            INNER JOIN product_template pt2
+                ON pt2.id = pp2.product_tmpl_id
+                AND pt1.company_id = pt2.company_id
+            WHERE
+                pp1.ean13 IS NOT NULL
+                AND pp1.ean13 != ''
+                AND pp1.id in %s
+            GROUP BY pp1.id
+            ORDER BY pp1.id""", (tuple(self.ids),))
+        return {x[0]: x[1] for x in self._cr.fetchall()}

BIN
models/product_product.pyc


BIN
static/description/icon.png


BIN
static/description/product_barcode_constrains.png


+ 2 - 0
tests/__init__.py

@@ -0,0 +1,2 @@
+# coding: utf-8
+from . import test_module

+ 69 - 0
tests/test_module.py

@@ -0,0 +1,69 @@
+# coding: utf-8
+# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop)
+# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from openerp.tests.common import TransactionCase
+from openerp.exceptions import ValidationError
+
+
+class TestModule(TransactionCase):
+
+    def setUp(self):
+        super(TestModule, self).setUp()
+        self.product_obj = self.env['product.product']
+        self.main_company = self.env.ref('base.main_company')
+
+    # Test Section
+    def _create_product(self, ean13, company_id):
+        return self.product_obj.create({
+            'name': 'Test',
+            'ean13': ean13,
+            'company_id': company_id,
+            'categ_id': self.env.ref('product.product_category_all').id
+        })
+
+    def test_01_create_duplicate(self):
+        """constrains of unicity for ean13 and company_id"""
+        # Create a first product with an ean13, shoud work
+        product = self._create_product('3760138839329', self.main_company.id)
+
+        # Create another product with the same ean13, in another company
+        # should work
+        self._create_product('3760138839329', False)
+
+        # Create again, with the same ean13, should fail
+        with self.assertRaises(ValidationError):
+            self._create_product('3760138839329', self.main_company.id)
+
+        # Create again, with the same ean13, should fail even if original
+        # product is disabled
+        product.active = False
+        with self.assertRaises(ValidationError):
+            self._create_product('3760138839329', self.main_company.id)
+
+    def test_02_copy_product(self):
+        """Copy product should not copy ean13 field."""
+        # Create a first product with an ean13, shoud work
+        product = self._create_product('3760138839329', self.main_company.id)
+        new_product = product.copy()
+        self.assertEqual(
+            new_product.ean13, False,
+            "Copy a product should set the ean13 field to false")
+
+    def test_03_duplicate_view(self):
+        """Check if existing duplicates are correctly displayed"""
+        # Create two products
+        product1 = self._create_product(False, self.main_company.id)
+        product2 = self._create_product(False, self.main_company.id)
+        sql_req = """
+            UPDATE product_product
+            SET ean13 = %s where id in %s
+        """
+        args = ('3760138839329', (product1.id, product2.id),)
+        self.env.cr.execute(sql_req, args)  # pylint: disable=invalid-commit
+        res = self.product_obj.search([('ean_duplicates_exist', '=', True)])
+
+        self.assertEqual(
+            len(res), 2,
+            "Incorrect result of the function _search_ean_duplicates_exist")

+ 19 - 0
views/action.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2015-Today GRAP (http://www.grap.coop)
+@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+ -->
+<openerp><data>
+
+    <record id="action_product_ean_duplicates" model="ir.actions.act_window">
+        <field name="name">Products (EAN Duplicates)</field>
+        <field name="type">ir.actions.act_window</field>
+        <field name="res_model">product.product</field>
+        <field name="view_type">form</field>
+        <field name="view_mode">tree</field>
+        <field name="view_id" ref="view_product_ean_duplicates"/>
+        <field name="context">{'search_default_ean_duplicates':1}</field>
+    </record>
+
+</data></openerp>

+ 14 - 0
views/menu.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2015-Today GRAP (http://www.grap.coop)
+@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+ -->
+<openerp><data>
+
+    <menuitem id="menu_product_ean_duplicates"
+        parent="base.menu_product"
+        action="action_product_ean_duplicates"
+        sequence="15"/>
+
+</data></openerp>

+ 32 - 0
views/view_product_product.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2015-Today GRAP (http://www.grap.coop)
+@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+ -->
+<openerp><data>
+
+    <record id="view_product_ean_duplicates" model="ir.ui.view">
+        <field name="model">product.product</field>
+        <field name="arch" type="xml">
+            <tree editable="bottom">
+                <field name="name"/>
+                <field name="ean13"/>
+                <field name="ean_duplicates_qty"/>
+                <field name="company_id" groups="base.group_multi_company"/>
+            </tree>
+        </field>
+    </record>
+
+    <record id="view_product_product_search" model="ir.ui.view">
+        <field name="model">product.product</field>
+        <field name="inherit_id" ref="product.product_search_form_view"/>
+        <field name="arch" type="xml">
+            <filter name="filter_to_sell" position="after">
+                <separator/>
+                <filter string="EAN Duplicates" name="ean_duplicates" domain="[('ean_duplicates_exist', '=', True)]"/>
+            </filter>
+        </field>
+    </record>
+
+</data></openerp>