Browse Source

Merge pull request #21 from sudhir-serpentcs/10.0

[IMP] Code improvements and bug fixes #20
Serpent Consulting Services Pvt. Ltd 7 years ago
parent
commit
18ce05cd12

+ 10 - 5
mass_editing/README.rst

@@ -16,10 +16,6 @@ This module provides the following features:
 
 * The video explaining the features and how-to for OpenERP Version 7 is here : http://www.youtube.com/watch?v=9BH0o74A748&feature=youtu.be
 
-* The video explaining the features and how-to for Odoo v9 is here: https://www.youtube.com/watch?v=hTng8BIbrQw
-
-* The video explaining the features and how-to for Odoo v10 is here: https://www.youtube.com/watch?v=W1EHfyvX9WI
-
 * For more details/customization/feedback contact us on contact@serpentcs.com
 
 Installation
@@ -37,6 +33,11 @@ To configure this module, you need to:
 Usage
 =====
 
+This module allows to add, update or remove the values of more than one records on the fly at the same time.
+
+.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
+   :alt: Try me on Runbot
+   :target: https://runbot.odoo-community.org/runbot/149/10.0
 
 As shown in figure you have to configure the object and fields for mass editing.
 
@@ -71,7 +72,11 @@ Bug Tracker
 Bugs are tracked on `GitHub Issues
 <https://github.com/OCA/server-tools/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.
+help us smashing it by providing a detailed and welcomed `feedback
+<https://github.com/OCA/
+server-tools/issues/new?body=module:%20
+server-tools%0Aversion:%20
+10.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
 
 Credits
 =======

+ 3 - 1
mass_editing/__manifest__.py

@@ -16,7 +16,9 @@
     'license': 'GPL-3 or any later version',
     'summary': 'Mass Editing',
     'uninstall_hook': 'uninstall_hook',
-    'depends': ['base'],
+    'depends': [
+        'base',
+    ],
     'data': [
         'security/ir.model.access.csv',
         'views/mass_editing_view.xml',

+ 4 - 7
mass_editing/models/ir_model_fields.py

@@ -2,7 +2,7 @@
 # © 2016 Serpent Consulting Services Pvt. Ltd. (support@serpentcs.com)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 
-from openerp import api, models
+from odoo import api, models
 
 
 class IrModelFields(models.Model):
@@ -12,16 +12,13 @@ class IrModelFields(models.Model):
     def search(self, args, offset=0, limit=0, order=None, count=False):
         model_domain = []
         for domain in args:
-            if (len(domain) > 2 and
-                    domain[0] == 'model_id' and
+            if (len(domain) > 2 and domain[0] == 'model_id' and
                     isinstance(domain[2], basestring) and
                     list(domain[2][1:-1])):
                 model_domain += [('model_id', 'in',
                                   map(int, domain[2][1:-1].split(',')))]
             else:
                 model_domain.append(domain)
-        return super(IrModelFields, self).search(model_domain,
-                                                 offset=offset,
-                                                 limit=limit,
-                                                 order=order,
+        return super(IrModelFields, self).search(model_domain, offset=offset,
+                                                 limit=limit, order=order,
                                                  count=count)

+ 3 - 2
mass_editing/models/mass_object.py

@@ -2,8 +2,8 @@
 # © 2016 Serpent Consulting Services Pvt. Ltd. (support@serpentcs.com)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 
-from openerp.exceptions import UserError
-from openerp import api, fields, models, _
+from odoo.exceptions import UserError
+from odoo import api, fields, models, _
 
 
 class MassObject(models.Model):
@@ -94,6 +94,7 @@ class MassObject(models.Model):
         self.unlink_action()
         return super(MassObject, self).unlink()
 
+    @api.multi
     @api.returns('self', lambda value: value.id)
     def copy(self, default=None):
         if default is None:

+ 3 - 3
mass_editing/tests/test_mass_editing.py

@@ -4,9 +4,9 @@
 
 import ast
 
-from openerp.tests import common
-from openerp.modules import registry
-from openerp.addons.mass_editing.hooks import uninstall_hook
+from odoo.tests import common
+from odoo.modules import registry
+from odoo.addons.mass_editing.hooks import uninstall_hook
 
 
 class TestMassEditing(common.TransactionCase):

+ 3 - 2
mass_editing/views/mass_editing_view.xml

@@ -14,7 +14,8 @@
                         </h1>
                         <group>
                             <group>
-                                <field name="model_id" required="1" attrs="{'readonly':[('ref_ir_act_window_id','!=',False)]}"/>
+                                <field name="model_id" required="1"
+                                       attrs="{'readonly':[('ref_ir_act_window_id','!=',False)]}"/>
                             </group>
                             <group>
                                 <field name="model_list" invisible="1"/>
@@ -41,7 +42,7 @@
                     <notebook colspan="4">
                         <page string="Fields">
                             <field name="field_ids" colspan="4" nolabel="1"
-                            domain="[('ttype', 'not in', ['reference', 'function']), ('model_id', 'in', model_list)]"/>
+                            domain="[('ttype', 'not in', ['refenrence', 'function', 'monetary']), ('model_id', 'in', model_list)]"/>
                         </page>
                         <page string="Advanced" attrs="{'invisible':[('ref_ir_act_window_id','=',False)]}">
                             <group colspan="2" col="2">

+ 145 - 32
mass_editing/wizard/mass_editing_wizard.py

@@ -2,10 +2,11 @@
 # © 2016 Serpent Consulting Services Pvt. Ltd. (support@serpentcs.com)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 
+import json
 from lxml import etree
 
-import openerp.tools as tools
-from openerp import api, models
+from odoo import tools
+from odoo import models, api
 
 
 class MassEditingWizard(models.TransientModel):
@@ -31,14 +32,6 @@ class MassEditingWizard(models.TransientModel):
                 'colspan': '6',
                 'col': '6',
             })
-            etree.SubElement(xml_group, 'label', {
-                'string': '',
-                'colspan': '2',
-            })
-            xml_group = etree.SubElement(xml_form, 'group', {
-                'colspan': '6',
-                'col': '6',
-            })
             model_obj = self.env[context.get('active_model')]
             field_info = model_obj.fields_get()
             for field in editing_data.field_ids:
@@ -48,7 +41,8 @@ class MassEditingWizard(models.TransientModel):
                         'type': 'selection',
                         'string': field_info[field.name]['string'],
                         'selection': [('set', 'Set'),
-                                      ('remove_m2m', 'Remove'),
+                                      ('remove_m2m', 'Remove Specific'),
+                                      ('remove_m2m_all', 'Remove All'),
                                       ('add', 'Add')]
                     }
                     xml_group = etree.SubElement(xml_group, 'group', {
@@ -68,30 +62,40 @@ class MassEditingWizard(models.TransientModel):
                         'name': field.name,
                         'colspan': '6',
                         'nolabel': '1',
-                        'attrs': ("{'invisible': [('selection__" +
-                                  field.name + "', '=', 'remove_m2m')]}"),
+                        'attrs': "{'invisible': [('selection__" +
+                        field.name + "', '=', 'remove_m2m')]}",
                     })
                 elif field.ttype == "one2many":
                     all_fields["selection__" + field.name] = {
                         'type': 'selection',
                         'string': field_info[field.name]['string'],
-                        'selection': [('set', 'Set'), ('remove', 'Remove')],
+                        'selection': [('set', 'Set'),
+                                      ('remove_o2m', 'Remove')],
                     }
                     all_fields[field.name] = {
                         'type': field.ttype,
                         'string': field.field_description,
                         'relation': field.relation,
                     }
+                    xml_group = etree.SubElement(xml_group, 'group', {
+                        'colspan': '6',
+                        'col': '6',
+                    })
+                    etree.SubElement(xml_group, 'separator', {
+                        'string': field_info[field.name]['string'],
+                        'colspan': '6',
+                    })
                     etree.SubElement(xml_group, 'field', {
                         'name': "selection__" + field.name,
-                        'colspan': '4',
+                        'colspan': '6',
+                        'nolabel': '1'
                     })
                     etree.SubElement(xml_group, 'field', {
                         'name': field.name,
                         'colspan': '6',
                         'nolabel': '1',
-                        'attrs': ("{'invisible':[('selection__" +
-                                  field.name + "', '=', 'remove_o2m')]}"),
+                        'attrs': "{'invisible':[('selection__" +
+                        field.name + "', '=', 'remove_o2m')]}",
                     })
                 elif field.ttype == "many2one":
                     all_fields["selection__" + field.name] = {
@@ -112,8 +116,48 @@ class MassEditingWizard(models.TransientModel):
                         'name': field.name,
                         'nolabel': '1',
                         'colspan': '4',
-                        'attrs': ("{'invisible':[('selection__" +
-                                  field.name + "', '=', 'remove')]}"),
+                        'attrs': "{'invisible':[('selection__" +
+                        field.name + "', '=', 'remove')]}",
+                    })
+                elif field.ttype == "float":
+                    all_fields["selection__" + field.name] = {
+                        'type': 'selection',
+                        'string': field_info[field.name]['string'],
+                        'selection': [('set', 'Set'),
+                                      ('val_add', '+'),
+                                      ('val_sub', '-'),
+                                      ('val_mul', '*'),
+                                      ('val_div', '/'),
+                                      ('remove', 'Remove')],
+                    }
+                    all_fields["set_selection_" + field.name] = {
+                        'type': 'selection',
+                        'string': 'Set calculation',
+                        'selection': [('set_fix', 'Fixed'),
+                                      ('set_per', 'Percentage')],
+                    }
+                    all_fields[field.name] = {
+                        'type': field.ttype,
+                        'string': field.field_description,
+                        'relation': field.relation,
+                    }
+                    etree.SubElement(xml_group, 'field', {
+                        'name': "selection__" + field.name,
+                        'colspan': '2',
+                    })
+                    etree.SubElement(xml_group, 'field', {
+                        'name': "set_selection_" + field.name,
+                        'nolabel': '1',
+                        'colspan': '1',
+                        'attrs': "{'invisible': [('selection__" +
+                        field.name + "', 'in', ('remove', 'set')]}",
+                    })
+                    etree.SubElement(xml_group, 'field', {
+                        'name': field.name,
+                        'nolabel': '1',
+                        'colspan': '3',
+                        'attrs': "{'invisible':[('selection__" +
+                        field.name + "', '=', 'remove')]}",
                     })
                 elif field.ttype == "char":
                     all_fields["selection__" + field.name] = {
@@ -133,8 +177,8 @@ class MassEditingWizard(models.TransientModel):
                     etree.SubElement(xml_group, 'field', {
                         'name': field.name,
                         'nolabel': '1',
-                        'attrs': ("{'invisible':[('selection__" +
-                                  field.name + "','=','remove')]}"),
+                        'attrs': "{'invisible':[('selection__" +
+                        field.name + "','=','remove')]}",
                         'colspan': '4',
                     })
                 elif field.ttype == 'selection':
@@ -151,8 +195,8 @@ class MassEditingWizard(models.TransientModel):
                         'name': field.name,
                         'nolabel': '1',
                         'colspan': '4',
-                        'attrs': ("{'invisible':[('selection__" +
-                                  field.name + "', '=', 'remove')]}"),
+                        'attrs': "{'invisible':[('selection__" +
+                        field.name + "', '=', 'remove')]}",
                     })
                     all_fields[field.name] = {
                         'type': field.ttype,
@@ -187,8 +231,8 @@ class MassEditingWizard(models.TransientModel):
                             'name': field.name,
                             'colspan': '6',
                             'nolabel': '1',
-                            'attrs': ("{'invisible':[('selection__" +
-                                      field.name + "','=','remove')]}"),
+                            'attrs': "{'invisible':[('selection__" +
+                            field.name + "','=','remove')]}",
                         })
                     else:
                         all_fields["selection__" + field.name] = {
@@ -203,8 +247,8 @@ class MassEditingWizard(models.TransientModel):
                         etree.SubElement(xml_group, 'field', {
                             'name': field.name,
                             'nolabel': '1',
-                            'attrs': ("{'invisible':[('selection__" +
-                                      field.name + "','=','remove')]}"),
+                            'attrs': "{'invisible':[('selection__" +
+                            field.name + "','=','remove')]}",
                             'colspan': '4',
                         })
             etree.SubElement(xml_form, 'separator', {
@@ -227,6 +271,30 @@ class MassEditingWizard(models.TransientModel):
             root = xml_form.getroottree()
             result['arch'] = etree.tostring(root)
             result['fields'] = all_fields
+            doc = etree.XML(result['arch'])
+            for field in editing_data.field_ids:
+                for node in doc.xpath("//field[@name='set_selection_" +
+                                      field.name + "']"):
+                    modifiers = json.loads(node.get("modifiers", '{}'))
+                    modifiers.update({'invisible': [(
+                        "selection__" + field.name, 'in', ('remove', 'set'))],
+                        'required': [("selection__" + field.name, 'in',
+                                      ('val_add', 'val_sub', 'val_mul',
+                                       'val_div'))]}
+                    )
+                    node.set("modifiers", json.dumps(modifiers))
+                for node in doc.xpath("//field[@name='" + field.name + "']"):
+                    modifiers = json.loads(node.get("modifiers", '{}'))
+                    attr_val = 'remove'
+                    if field.ttype == "many2many":
+                        attr_val = 'remove_m2m_all'
+                    elif field.ttype == "one2many":
+                        attr_val = 'remove_o2m'
+                    modifiers.update({'invisible': [
+                        ("selection__" + field.name, '=', attr_val)
+                    ]})
+                    node.set("modifiers", json.dumps(modifiers))
+            result['arch'] = etree.tostring(doc)
         return result
 
     @api.model
@@ -234,23 +302,68 @@ class MassEditingWizard(models.TransientModel):
         if (self._context.get('active_model') and
                 self._context.get('active_ids')):
             model_obj = self.env[self._context.get('active_model')]
+            model_rec = model_obj.browse(self._context.get('active_ids'))
             values = {}
             for key, val in vals.items():
                 if key.startswith('selection_'):
                     split_key = key.split('__', 1)[1]
+                    set_val = vals.get('set_selection_' + split_key)
                     if val == 'set':
                         values.update({split_key: vals.get(split_key, False)})
                     elif val == 'remove':
                         values.update({split_key: False})
                     elif val == 'remove_m2m':
+                        if vals.get(split_key):
+                            m2m_list = []
+                            for m2m_id in vals.get(split_key)[0][2]:
+                                m2m_list.append((3, m2m_id))
+                            values.update({split_key: m2m_list})
+                    elif val in ['remove_o2m', 'remove_m2m_all']:
                         values.update({split_key: [(5, 0, [])]})
                     elif val == 'add':
-                        m2m_list = []
-                        for m2m_id in vals.get(split_key, False)[0][2]:
-                            m2m_list.append((4, m2m_id))
-                        values.update({split_key: m2m_list})
+                        if vals.get(split_key, False):
+                            m2m_list = []
+                            for m2m_id in vals.get(split_key)[0][2]:
+                                m2m_list.append((4, m2m_id))
+                            values.update({split_key: m2m_list})
+
+                    # Mathematical operations
+                    elif val in ['val_add', 'val_sub', 'val_mul', 'val_div']:
+                        split_val = vals.get(split_key, 0.0)
+                        for data in model_rec:
+                            split_key_data = data[split_key]
+                            tot_val = 0
+                            # Addition
+                            if val == 'val_add':
+                                if set_val == 'set_fix':
+                                    tot_val = split_key_data + split_val
+                                elif set_val == 'set_per':
+                                    tot_val = split_key_data +\
+                                        (split_key_data * split_val) / 100.0
+                            # Subtraction
+                            elif val == 'val_sub':
+                                if set_val == 'set_fix':
+                                    tot_val = split_key_data - split_val
+                                elif set_val == 'set_per':
+                                    tot_val = split_key_data -\
+                                        (split_key_data * split_val) / 100.0
+                            # Multiplication
+                            elif val == 'val_mul':
+                                if set_val == 'set_fix':
+                                    tot_val = split_key_data * split_val
+                                elif set_val == 'set_per':
+                                    tot_val = split_key_data *\
+                                        (split_key_data * split_val) / 100
+                            # Division
+                            elif val == 'val_div':
+                                if set_val == 'set_fix':
+                                    tot_val = split_key_data / split_val
+                                elif set_val == 'set_per':
+                                    tot_val = split_key_data /\
+                                        (split_key_data * split_val) / 100
+                            data.write({split_key: tot_val})
             if values:
-                model_obj.browse(self._context.get('active_ids')).write(values)
+                model_rec.write(values)
         return super(MassEditingWizard, self).create({})
 
     @api.multi