From 244a7294e4c1db6df0234eafb5d8b0b16faeaf36 Mon Sep 17 00:00:00 2001 From: Kiplangat Dan Date: Fri, 11 Mar 2022 14:40:46 +0300 Subject: [PATCH 01/25] [ADD] mass_merge --- mass_merge/README.rst | 37 +++++ mass_merge/__init__.py | 10 ++ mass_merge/__manifest__.py | 26 ++++ mass_merge/models/__init__.py | 3 + mass_merge/models/merge_editing.py | 81 ++++++++++ mass_merge/security/ir.model.access.csv | 2 + mass_merge/security/merge_security.xml | 8 + mass_merge/static/description/icon.png | Bin 0 -> 28984 bytes mass_merge/static/description/index.html | 104 +++++++++++++ mass_merge/tools.py | 12 ++ mass_merge/views/merge_editing_view.xml | 67 +++++++++ mass_merge/wizard/__init__.py | 8 + mass_merge/wizard/merge_fuse_wizard.py | 182 +++++++++++++++++++++++ mass_merge/wizard/merge_fuse_wizard.xml | 36 +++++ 14 files changed, 576 insertions(+) create mode 100644 mass_merge/README.rst create mode 100644 mass_merge/__init__.py create mode 100644 mass_merge/__manifest__.py create mode 100644 mass_merge/models/__init__.py create mode 100644 mass_merge/models/merge_editing.py create mode 100644 mass_merge/security/ir.model.access.csv create mode 100644 mass_merge/security/merge_security.xml create mode 100644 mass_merge/static/description/icon.png create mode 100644 mass_merge/static/description/index.html create mode 100644 mass_merge/tools.py create mode 100755 mass_merge/views/merge_editing_view.xml create mode 100644 mass_merge/wizard/__init__.py create mode 100644 mass_merge/wizard/merge_fuse_wizard.py create mode 100644 mass_merge/wizard/merge_fuse_wizard.xml diff --git a/mass_merge/README.rst b/mass_merge/README.rst new file mode 100644 index 0000000000..1c08e64b6f --- /dev/null +++ b/mass_merge/README.rst @@ -0,0 +1,37 @@ +.. image:: https://img.shields.io/badge/license-LGPL--3-blue.png + :target: https://www.gnu.org/licenses/lgpl + :alt: License: LGPL-3 + +Mass Merge Records +================== +This module is a general purpose module that merges records that may be similar +or reflect similar properties when created. + +Considerations +-------------- + +Technical Explanation +--------------------- +For demonstration there can exist a customer with two or more record entries that +are same apart from difference in names. Among the records there could be caps +in the name characters. e.g +** 1.) Paul +** 2.) PAul +** 3.) pAuL +the above records are just one "__Paul__" so in this case you can merge the +records into one. + +Functional Usage +---------------- + +TODO: Put here some functional examples. + +Known issues / Roadmap +---------------------- + +- Tests: merge 2, 3 countries; check if partner countries have changed, + check if ir_model_data was updated +- On merge wizard, show a visual link that opens the ref in a pop window + (such as web_tree_many2one_clickable offers) +- Generic support for '_inherits' models: when one is merged, merge the other + (now we only have specific support for product.product/product.template) diff --git a/mass_merge/__init__.py b/mass_merge/__init__.py new file mode 100644 index 0000000000..499d69e1c1 --- /dev/null +++ b/mass_merge/__init__.py @@ -0,0 +1,10 @@ +# coding: utf-8 +# OpenERP, Open Source Management Solution +# Copyright (C) 2012 Serpent Consulting Services () +# Copyright (C) 2010-Today OpenERP SA () +# Copyright (c) 2019-Today Sunflower IT () +# License GNU General Public License see + +from . import models +from . import tools +from . import wizard diff --git a/mass_merge/__manifest__.py b/mass_merge/__manifest__.py new file mode 100644 index 0000000000..bd6e0286c1 --- /dev/null +++ b/mass_merge/__manifest__.py @@ -0,0 +1,26 @@ +# coding: utf-8 +# Copyright (C) 2010-2019 Today OpenERP SA () +# Sunflower IT () +# programmed by: Oscar Alcala: oscar@vauxoo.com +# programmed by: Jose Morales: jose@vauxoo.com +# programmed by: Sunflower IT: info@sunflowerweb.nl +# License GNU General Public License see +{ + "name": "Mass Merge Records", + "version": "14.0.1.0.1", + "author": "Vauxoo", + "category": "Tools", + "website": "http://www.serpentcs.com", + "license": "AGPL-3", + 'depends': [ + 'base' + ], + 'data': [ + 'security/merge_security.xml', + 'security/ir.model.access.csv', + 'wizard/merge_fuse_wizard.xml', + 'views/merge_editing_view.xml', + ], + 'installable': True, + 'application': True, +} diff --git a/mass_merge/models/__init__.py b/mass_merge/models/__init__.py new file mode 100644 index 0000000000..763de381e4 --- /dev/null +++ b/mass_merge/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import merge_editing diff --git a/mass_merge/models/merge_editing.py b/mass_merge/models/merge_editing.py new file mode 100644 index 0000000000..17c30598d5 --- /dev/null +++ b/mass_merge/models/merge_editing.py @@ -0,0 +1,81 @@ +# coding: utf-8 +# License GNU General Public License see +from odoo import fields, models, api, _ +from odoo.exceptions import UserError + + +class MergeObject(models.Model): + _name = "merge.object" + + name = fields.Char("Name", size=64, required=True, index=True) + model_id = fields.Many2one('ir.model', 'Model', required=True, index=True) + ref_ir_act_server_fuse = fields.Many2one( + 'ir.actions.server', + 'Sidebar fuse server action', readonly=True, + help="Sidebar action to make this template\ + available on records of the related document\ + model") + ref_ir_value_fuse = fields.Many2one( + 'ir.values', 'Sidebar fuse button', + readonly=True, help="Sidebar button to\ + open the sidebar action") + model_list = fields.Char('Model List', size=256) + + @api.onchange('model_id') + def onchange_model(self): + if self.model_id: + model_obj = self.env['ir.model'] + model_data = model_obj.browse(self.model_id.id) + self.model_list = "[" + str(self.model_id) + "" + active_model_obj = self.env[model_data.model] + if active_model_obj._inherits: + for key, val in active_model_obj._inherits.items(): + model_ids = model_obj.search([('model', '=', key)]) + if model_ids: + self.model_list += "," + str(model_ids[0]) + "" + self.model_list += "]" + + @api.multi + def create_action_fuse(self): + vals = {} + action_obj = self.env['ir.actions.server'] + for data in self: + src_obj = data.model_id.model + button_name = _('Mass Fuse (%s)') % data.name + vals['ref_ir_act_server_fuse'] = action_obj.create({ + 'name': button_name, + 'type': 'ir.actions.server', + 'model_id': self.env.ref( + 'merge_editing.model_merge_fuse_wizard').id, + 'state': 'code', + 'code': 'action = model._get_wizard_action()', + 'condition': True, + }) + vals['ref_ir_value_fuse'] = self.env['ir.values'].create({ + 'name': button_name, + 'model': src_obj, + 'key': 'action', + 'key2': 'client_action_multi', + 'value': "ir.actions.server," + str( + vals['ref_ir_act_server_fuse'].id), + }) + self.write({ + 'ref_ir_act_server_fuse': vals.get('ref_ir_act_server_fuse', False).id, + 'ref_ir_value_fuse': vals.get('ref_ir_value_fuse', False).id, + }) + return True + + @api.multi + def unlink_fuse_action(self): + for template in self: + try: + if template.ref_ir_act_server_fuse: + self.env['ir.actions.server'].search( + [('id', '=', template.ref_ir_act_server_fuse.id)]).unlink() + if template.ref_ir_value_fuse: + ir_values_obj = self.env['ir.values'] + ir_values_obj.search( + [('id', '=', template.ref_ir_value_fuse.id)]).unlink() + except: + raise UserError(_("Deletion of the action record failed.")) + return True diff --git a/mass_merge/security/ir.model.access.csv b/mass_merge/security/ir.model.access.csv new file mode 100644 index 0000000000..df68ff4b04 --- /dev/null +++ b/mass_merge/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"merge_manager","merge.object","model_merge_object","merge_editing.group_merge_editing",1,1,1,1 diff --git a/mass_merge/security/merge_security.xml b/mass_merge/security/merge_security.xml new file mode 100644 index 0000000000..d6090961f3 --- /dev/null +++ b/mass_merge/security/merge_security.xml @@ -0,0 +1,8 @@ + + + + + Mass Merge + + + diff --git a/mass_merge/static/description/icon.png b/mass_merge/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..58dd9665bc24bb487debd12904c5799461acfedb GIT binary patch literal 28984 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 literal 0 HcmV?d00001 diff --git a/mass_merge/static/description/index.html b/mass_merge/static/description/index.html new file mode 100644 index 0000000000..6b451d5704 --- /dev/null +++ b/mass_merge/static/description/index.html @@ -0,0 +1,104 @@ + + + +
+
+

+ AutoMerge Records +

+

Module Description

+

+ This module is a general purpose module that merges records that may + be similar + or reflect similar properties when created. +

+
+

+ +

+
+
+

Technical Explanation

+

For demonstration there can exist a customer with two or more + record entries that + are same apart from difference in names. Among the records + there could be caps + in the name characters. e.g +

    +
  • PauL
  • +
  • paUL
  • +
  • pAul
  • +
  • PAUL
  • +
+

+ the above records are just one "Paul" so in this case + you can merge the + records into one. +

+

+ + +

+ TODO: Put here some technical examples. +

+
+
+

+ Functional Usage +

+

+ TODO: Put here some functional examples. +

+
+
+
+
+
+
+

Do you need help?

+

+ Let's offer you the best services! +

+

+ Contact us by our official channels. +

+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+ + + + +
+
+
+
diff --git a/mass_merge/tools.py b/mass_merge/tools.py new file mode 100644 index 0000000000..bd36135b2e --- /dev/null +++ b/mass_merge/tools.py @@ -0,0 +1,12 @@ +# coding: utf-8 +# Copyright (c) 2019-Today Sunflower IT () +# License GNU General Public License see + +def is_table(cr, table): + """ Check whether a certain table exists and is not a view """ + cr.execute( + "SELECT 1 FROM pg_class WHERE relname = %s AND relkind = 'r'", + (table,) + ) + return cr.fetchone() + diff --git a/mass_merge/views/merge_editing_view.xml b/mass_merge/views/merge_editing_view.xml new file mode 100755 index 0000000000..86a95e6abc --- /dev/null +++ b/mass_merge/views/merge_editing_view.xml @@ -0,0 +1,67 @@ + + + + + merge.object.form + merge.object + form + +
+ + + + + + + + + + +