← Back to team overview

openerp-community team mailing list archive

[Merge] lp:~akretion-team/openerp-product-attributes/mixin-extraction into lp:openerp-product-attributes

 

Raphaël Valyi - http://www.akretion.com has proposed merging lp:~akretion-team/openerp-product-attributes/mixin-extraction into lp:openerp-product-attributes with lp:~akretion-team/openerp-product-attributes/polymorphic-relations as a prerequisite.

Requested reviews:
  Product Core Editors (product-core-editors)

For more details, see:
https://code.launchpad.net/~akretion-team/openerp-product-attributes/mixin-extraction/+merge/151333

See commit messages:
no more code but but code is now split in two modules: product_custom_attributes and base_custom_attributes that can be reused in any OpenERP object (partner, project, production order etc...). That last module may be moved to another branch later if it makes more sense.
-- 
https://code.launchpad.net/~akretion-team/openerp-product-attributes/mixin-extraction/+merge/151333
Your team OpenERP Community is subscribed to branch lp:openerp-product-attributes.
=== renamed directory 'product_custom_attributes' => 'base_custom_attributes'
=== modified file 'base_custom_attributes/__init__.py'
--- product_custom_attributes/__init__.py	2012-08-03 14:33:51 +0000
+++ base_custom_attributes/__init__.py	2013-03-02 00:14:23 +0000
@@ -22,9 +22,7 @@
 
 
 import ir_model
-import product_attribute
-import product
-import wizard
+import custom_attributes
 
 
 

=== modified file 'base_custom_attributes/__openerp__.py'
--- product_custom_attributes/__openerp__.py	2013-02-11 22:58:10 +0000
+++ base_custom_attributes/__openerp__.py	2013-03-02 00:14:23 +0000
@@ -1,7 +1,7 @@
 # -*- encoding: utf-8 -*-
 ###############################################################################
 #                                                                             #
-#   product_custom_attributes for OpenERP                                      #
+#   base_custom_attributes for OpenERP                                        #
 #   Copyright (C) 2011 Akretion Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>  #
 #                                                                             #
 #   This program is free software: you can redistribute it and/or modify      #
@@ -22,23 +22,19 @@
 
 
 {
-    'name': 'product_custom_attributes',
+    'name': 'base_custom_attributes',
     'version': '0.1',
     'category': 'Generic Modules/Others',
     'license': 'AGPL-3',
-    'description': """This module adds the possibility to easily create custom fields on products.
-Each product can be linked to an attribute set (like camera, fridge...).
-Each attribute has custom fields (for example, you don't need the same field for a frigde and a camera).
+    'description': """This module adds the possibility to easily create custom attributes in any OpenERP business object. See the product_custom_attributes module for instance.
     """,
     'author': 'Akretion',
     'website': 'http://www.akretion.com/',
-    'depends': ['product'],
+    'depends': ['base'],
     'init_xml': [],
     'update_xml': [
            'security/ir.model.access.csv',
-           'product_attribute_view.xml',
-           'product_view.xml',
-           'wizard/open_product_by_attribute_set.xml',
+           'custom_attributes_view.xml',
     ],
     'demo_xml': [],
     'installable': True,

=== renamed file 'product_custom_attributes/product_attribute.py' => 'base_custom_attributes/custom_attributes.py'
--- product_custom_attributes/product_attribute.py	2013-03-02 00:14:23 +0000
+++ base_custom_attributes/custom_attributes.py	2013-03-02 00:14:23 +0000
@@ -1,8 +1,9 @@
 # -*- encoding: utf-8 -*-
 ###############################################################################
 #                                                                             #
-#   product_custom_attributes for OpenERP                                      #
+#   base_custom_attributes for OpenERP                                        #
 #   Copyright (C) 2011 Akretion Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>  #
+#   Copyright (C) 2013 Akretion Raphaël VALYI <raphael.valyi@xxxxxxxxxxxx>    #
 #                                                                             #
 #   This program is free software: you can redistribute it and/or modify      #
 #   it under the terms of the GNU Affero General Public License as            #
@@ -35,7 +36,7 @@
     _columns = {
         'name': fields.char('Name', size=128, translate=True, required=True),
         'value_ref': fields.reference('Reference', selection=[], size=128),
-        'attribute_id': fields.many2one('product.attribute', 'Product Attribute', required=True),
+        'attribute_id': fields.many2one('custom.attribute', 'Product Attribute', required=True),
         'sequence': fields.integer('Sequence'),
     }
 
@@ -51,7 +52,7 @@
     _rec_name = 'attribute_id'
 
     _columns = {
-        'attribute_id': fields.many2one('product.attribute', 'Product Attribute', required=True),
+        'attribute_id': fields.many2one('custom.attribute', 'Product Attribute', required=True),
     }
 
     _defaults = {
@@ -62,7 +63,7 @@
         return True
 
     def create(self, cr, uid, vals, context=None):
-        attr_obj = self.pool.get("product.attribute")
+        attr_obj = self.pool.get("custom.attribute")
         attr = attr_obj.browse(cr, uid, vals['attribute_id'])
         op_ids = [op.id for op in attr.option_ids]
         opt_obj = self.pool.get("attribute.option")
@@ -81,7 +82,7 @@
     def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
         res = super(attribute_option_wizard, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)
         if context and context.get("attribute_id"):
-            attr_obj = self.pool.get("product.attribute")
+            attr_obj = self.pool.get("custom.attribute")
             model_id = attr_obj.read(cr, uid, [context.get("attribute_id")], ['relation_model_id'])[0]['relation_model_id'][0]
             relation = self.pool.get("ir.model").read(cr, uid, [model_id], ["model"])[0]["model"]
             res['fields'].update({'option_ids': {
@@ -100,11 +101,44 @@
         return res
 
 
-class product_attribute(Model):
-    _name = "product.attribute"
+class custom_attribute(Model):
+    _name = "custom.attribute"
     _description = "Product Attribute"
     _inherits = {'ir.model.fields': 'field_id'}
 
+    def _build_attribute_field(self, cr, uid, page, attribute, context=None):
+        parent = page
+        kwargs = {'name': "%s" % attribute.name}
+        if attribute.ttype == 'many2many':
+            parent = etree.SubElement(page, 'group', colspan="2", col="4")
+            #FIXME the following isn't displayed in v7:
+            sep = etree.SubElement(parent, 'separator',
+                                    string="%s" % attribute.field_description, colspan="4")
+            kwargs['nolabel'] = "1"
+        if attribute.ttype in ['many2one', 'many2many']:
+            if attribute.relation_model_id:
+                if attribute.domain:
+                    kwargs['domain'] = attribute.domain
+                else:
+                    ids = [op.value_ref.id for op in attribute.option_ids]
+                    kwargs['domain'] = "[('id', 'in', %s)]" % ids
+            else:
+                kwargs['domain'] = "[('attribute_id', '=', %s)]" % attribute.attribute_id.id
+        field = etree.SubElement(parent, 'field', **kwargs)
+        return parent
+
+    def _build_attributes_notebook(self, cr, uid, attribute_group_ids, context=None):
+        notebook = etree.Element('notebook', name="attributes_notebook", colspan="4")
+        toupdate_fields = []
+        grp_obj = self.pool.get('attribute.group')
+        for group in grp_obj.browse(cr, uid, attribute_group_ids, context=context):
+            page = etree.SubElement(notebook, 'page', string=group.name.capitalize())
+            for attribute in group.attribute_ids:
+                if attribute.name not in toupdate_fields:
+                    toupdate_fields.append(attribute.name)
+                    self._build_attribute_field(cr, uid, page, attribute, context=context)
+        return notebook, toupdate_fields
+
     def relation_model_id_change(self, cr, uid, ids, relation_model_id, option_ids, context=None):
         "removed selected options as they would be inconsistent" 
         return {'value': {'option_ids': [(2, i[1]) for i in option_ids]}}
@@ -136,9 +170,6 @@
         'serialized': fields.boolean('Field serialized',
                                      help="If serialized, the field will be stocked in the serialized field: "
                                      "attribute_custom_tmpl or attribute_custom_variant depending on the field based_on"),
-        'based_on': fields.selection([('product_template','Product Template'),
-                                      ('product_product','Product Variant')],
-                                     'Based on', required=True),
         'option_ids': fields.one2many('attribute.option', 'attribute_id', 'Attribute Options'),
         'create_date': fields.datetime('Created date', readonly=True),
         'relation_model_id': fields.many2one('ir.model', 'Model'),
@@ -147,17 +178,25 @@
 
     def create(self, cr, uid, vals, context=None):
         if vals.get('relation_model_id'):
-            relation = self.pool.get('ir.model').read(cr, uid, [vals.get('relation_model_id')], ['model'])[0]['model']
+            relation = self.pool.get('ir.model').read(cr, uid,
+            [vals.get('relation_model_id')], ['model'])[0]['model']
         else:
             relation = 'attribute.option'
-        if vals.get('based_on') == 'product_template':
-            vals['model_id'] = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'product.template')], context=context)[0]
-            serial_name = 'attribute_custom_tmpl'
-        else:
-            vals['model_id'] = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'product.product')], context=context)[0]
-            serial_name = 'attribute_custom_variant'
         if vals.get('serialized'):
-            vals['serialization_field_id'] = self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', serial_name)], context=context)[0]
+            field_obj = self.pool.get('ir.model.fields')
+            serialized_ids = field_obj.search(cr, uid,
+            [('ttype', '=', 'serialized'), ('model_id', '=', vals['model_id']),
+            ('name', '=', 'x_custom_json_attrs')], context=context)
+            if serialized_ids:
+                vals['serialization_field_id'] = serialized_ids[0]
+            else:
+                f_vals = {
+                    'name': 'x_custom_json_attrs',
+                    'field_description': 'Serialized JSON Attributes', 
+                    'ttype': 'serialized',
+                    'model_id': vals['model_id'],
+                }
+                vals['serialization_field_id'] = field_obj.create(cr, uid, f_vals, {'manual': True})
         if vals['attribute_type'] == 'select':
             vals['ttype'] = 'many2one'
             vals['relation'] = relation
@@ -169,7 +208,7 @@
         else:
             vals['ttype'] = vals['attribute_type']
         vals['state'] = 'manual'
-        return super(product_attribute, self).create(cr, uid, vals, context)
+        return super(custom_attribute, self).create(cr, uid, vals, context)
 
     def onchange_field_description(self, cr, uid, ids, field_description, context=None):
         name = 'x_'
@@ -182,51 +221,30 @@
             name = 'x_%s' % name
         return  {'value' : {'name' : unidecode(name)}}
 
+
+class attribute_group(Model):
+    _name= "attribute.group"
+    _description = "Attribute Group"
+    _order="sequence"
+
+    _columns = {
+        'name': fields.char('Name', size=128, required=True),
+        'sequence': fields.integer('Sequence'),
+        'attribute_ids': fields.one2many('attribute.location', 'attribute_group_id', 'Attributes'),
+    }
+
 class attribute_location(Model):
     _name = "attribute.location"
     _description = "Attribute Location"
     _order="sequence"
-    _inherits = {'product.attribute': 'attribute_id'}
+    _inherits = {'custom.attribute': 'attribute_id'}
 
 
     def _get_attribute_loc_from_group(self, cr, uid, ids, context=None):
         return self.pool.get('attribute.location').search(cr, uid, [('attribute_group_id', 'in', ids)], context=context)
 
     _columns = {
-        'attribute_id': fields.many2one('product.attribute', 'Product Attribute', required=True, ondelete="cascade"),
-        'attribute_set_id': fields.related('attribute_group_id', 'attribute_set_id', type='many2one', relation='attribute.set', string='Attribute Set', readonly=True,
-store={
-            'attribute.group': (_get_attribute_loc_from_group, ['attribute_set_id'], 10),
-        }),
+        'attribute_id': fields.many2one('custom.attribute', 'Product Attribute', required=True, ondelete="cascade"),
         'attribute_group_id': fields.many2one('attribute.group', 'Attribute Group', required=True),
         'sequence': fields.integer('Sequence'),
         }
-
-
-
-class attribute_group(Model):
-    _name= "attribute.group"
-    _description = "Attribute Group"
-    _order="sequence"
-
-    _columns = {
-        'name': fields.char('Name', size=128, required=True),
-        'attribute_set_id': fields.many2one('attribute.set', 'Attribute Set'),
-        'attribute_ids': fields.one2many('attribute.location', 'attribute_group_id', 'Attributes'),
-        'sequence': fields.integer('Sequence'),
-    }
-
-    def create(self, cr, uid, vals, context=None):
-        for attribute in vals['attribute_ids']:
-            if vals.get('attribute_set_id') and attribute[2] and not attribute[2].get('attribute_set_id'):
-                attribute[2]['attribute_set_id'] = vals['attribute_set_id']
-        return super(attribute_group, self).create(cr, uid, vals, context)
-
-class attribute_set(Model):
-    _name = "attribute.set"
-    _description = "Attribute Set"
-    _columns = {
-        'name': fields.char('Name', size=128, required=True),
-        'attribute_group_ids': fields.one2many('attribute.group', 'attribute_set_id', 'Attribute Groups'),
-        }
-

=== renamed file 'product_custom_attributes/product_attribute_view.xml' => 'base_custom_attributes/custom_attributes_view.xml'
--- product_custom_attributes/product_attribute_view.xml	2013-03-02 00:14:23 +0000
+++ base_custom_attributes/custom_attributes_view.xml	2013-03-02 00:14:23 +0000
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  product_custom_attributes for OpenERP
+  base_custom_attributes for OpenERP
   Copyright (C) 2011-2013 Akretion (http://www.akretion.com/)
   @author: Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>
   The licence is in the file __openerp__.py
@@ -9,81 +9,15 @@
 <openerp>
     <data>
 
-        <menuitem
-            id="menu_attribute_in_config_product" name="Attributes"
-            parent="product.prod_config_main" sequence="20"/>
-
-        <record id="attribute_set_form_view" model="ir.ui.view">
-            <field name="name">attribute.set.form</field>
-            <field name="model">attribute.set</field>
-            <field name="arch" type="xml">
-                <form string="Attribute Set">
-                    <field name="name" colspan="4"/>
-                    <field name="attribute_group_ids" colspan="4" >
-                        <form string="Attribute Groups">
-                            <field name="name" />
-                            <field name="sequence" />
-                            <field name="attribute_ids" colspan="4">
-                                <form string="Attribute Location">
-                                    <field name="attribute_id" />
-                                    <field name="sequence" />
-                                </form>
-                                <tree string="Attribute Location">
-                                    <field name="attribute_id" />
-                                </tree>
-                            </field>
-                        </form>
-                        <tree string="Attribute Groups">
-                            <field name="name" />
-                        </tree>
-                    </field>
-                </form>
-            </field>
-        </record>
-
-        <record id="attribute_group_form_view" model="ir.ui.view">
-            <field name="name">attribute.group.form</field>
-            <field name="model">attribute.group</field>
-            <field name="arch" type="xml">
-                <form string="Attribute Group">
-                    <field name="name" />
-                    <field name="sequence" />
-                    <field name="attribute_set_id" />
-                    <field name="attribute_ids" colspan="4" nolabel="1">
-                        <form string="Attribute Location">
-                            <field name="attribute_id" />
-                            <field name="sequence" />
-                        </form>
-                        <tree string="Attribute Location">
-                            <field name="attribute_id" />
-                        </tree>
-                    </field>
-                </form>
-            </field>
-        </record>
-
-        <record id="attribute_location_form_view" model="ir.ui.view">
-            <field name="name">attribute.location.form</field>
-            <field name="model">attribute.location</field>
-            <field name="arch" type="xml">
-                <form string="Attribute Location">
-                    <field name="attribute_id" />
-                    <field name="sequence" />
-                    <field name="attribute_group_id" />
-                    <field name="attribute_set_id" />
-                </form>
-            </field>
-        </record>
-
-        <record id="product_attribute_form_view" model="ir.ui.view">
-            <field name="name">product.attribute.form</field>
-            <field name="model">product.attribute</field>
+        <record id="custom_attribute_form_view" model="ir.ui.view">
+            <field name="name">custom.attribute.form</field>
+            <field name="model">custom.attribute</field>
             <field name="arch" type="xml">
                 <form string="Product Attribute">
                     <field name="field_description" on_change="onchange_field_description(field_description, context)"/>
                     <field name="name" attrs="{'readonly':[('create_date', '!=', False)]}" on_change="onchange_name(name, context)"/>
                     <field name="attribute_type" />
-                    <field name="based_on" />
+                    <field name="model_id" />
                     <field name="serialized" />
                     <field name="size" attrs="{'invisible':[('attribute_type', '!=', 'char')]}"/>
                     <field name="translate" attrs="{'invisible':[('attribute_type', 'not in', ('char', 'text'))]}"/>
@@ -91,8 +25,7 @@
                     <group colspan="4" attrs="{'invisible':[('attribute_type', 'not in', ['select', 'multiselect'])]}">
                     <field name="relation_model_id" on_change="relation_model_id_change(relation_model_id, option_ids, context)"/>
                     <field name="domain" attrs="{'invisible':[('relation_model_id', '=', False)]}"/>
-                    <group colspan="4" attrs="{'invisible':[('domain', '!=', False)]}">
-                    <button name="button_add_options" attrs="{'invisible':[('relation_model_id', '=', False)]}" type="object" string="Change Options"/>
+                    <button name="button_add_options" attrs="{'invisible':[('relation_model_id', '=', False), ('domain', '!=', False)]}" type="object" string="Change Options"/>
                     <field name="option_ids" colspan="4" nolabel="1">
                         <tree string="Attribute Options" editable="top">
                             <field name="sequence" invisible="1"/>
@@ -100,7 +33,6 @@
                         </tree>
                     </field>
                     </group>
-                    </group>
                     <field name="create_date" invisible="1"/>
                 </form>
             </field>
@@ -124,6 +56,7 @@
             <field name="model">attribute.option</field>
             <field name="arch" type="xml">
                 <form string="Attribute Option" col="6">
+                    <field name="name" colspan="2"/>
                     <field name="value_ref" colspan="2"/>
                     <field name="sequence" colspan="2"/>
                     <field name="attribute_id" colspan="2"/>
@@ -131,13 +64,20 @@
             </field>
         </record>
 
-        <record id="attribute_set_tree_view" model="ir.ui.view">
-            <field name="name">attribute.set.tree</field>
-            <field name="model">attribute.set</field>
+        <record id="attribute_group_form_view" model="ir.ui.view">
+            <field name="name">attribute.group.form</field>
+            <field name="model">attribute.group</field>
             <field name="arch" type="xml">
-                <tree string="Attribute Set" >
+                <form string="Attribute Group">
                     <field name="name" />
-                </tree>
+                    <field name="sequence" />
+                    <field name="attribute_ids" colspan="4" nolabel="1">
+                        <form string="Attribute Location">
+                            <field name="attribute_id" />
+                            <field name="sequence" />
+                        </form>
+                    </field>
+                </form>
             </field>
         </record>
 
@@ -148,27 +88,13 @@
                 <tree string="Attribute Group">
                     <field name="name" />
                     <field name="sequence" />
-                    <field name="attribute_set_id" />
-                </tree>
-            </field>
-        </record>
-
-        <record id="attribute_location_tree_view" model="ir.ui.view">
-            <field name="name">attribute.location.tree</field>
-            <field name="model">attribute.location</field>
-            <field name="arch" type="xml">
-                <tree string="Attribute Location">
-                    <field name="attribute_id" />
-                    <field name="sequence" />
-                    <field name="attribute_set_id" />
-                    <field name="attribute_group_id" />
-                </tree>
-            </field>
-        </record>
-
-        <record id="product_attribute_tree_view" model="ir.ui.view">
-            <field name="name">product.attribute.tree</field>
-            <field name="model">product.attribute</field>
+                </tree>
+            </field>
+        </record>
+
+        <record id="custom_attribute_tree_view" model="ir.ui.view">
+            <field name="name">custom.attribute.tree</field>
+            <field name="model">custom.attribute</field>
             <field name="arch" type="xml">
                 <tree string="Product Attribute">
                     <field name="name" />
@@ -201,42 +127,9 @@
             </field>
         </record>
 
-        <record id="view_attribute_set_search" model="ir.ui.view">
-            <field name="name">attribute.set.list</field>
-            <field name="model">attribute.set</field>
-            <field name="arch" type="xml">
-                <search string="Search Attribute Sets">
-                    <field name="name"/>
-               </search>
-            </field>
-        </record>
-
-        <record id="view_attribute_group_search" model="ir.ui.view">
-            <field name="name">attribute.group.list</field>
-            <field name="model">attribute.group</field>
-            <field name="arch" type="xml">
-                <search string="Search Attribute Groups">
-                    <field name="name"/>
-                    <field name="attribute_set_id"/>
-               </search>
-            </field>
-        </record>
-
-        <record id="view_attribute_location_search" model="ir.ui.view">
-            <field name="name">attribute.location.list</field>
-            <field name="model">attribute.location</field>
-            <field name="arch" type="xml">
-                <search string="Search Attribute Locations">
-                    <field name="name"/>
-                    <field name="attribute_set_id"/>
-                    <field name="attribute_group_id"/>
-               </search>
-            </field>
-        </record>
-
-        <record id="view_product_attribute_search" model="ir.ui.view">
-            <field name="name">product.attribute.list</field>
-            <field name="model">product.attribute</field>
+        <record id="view_custom_attribute_search" model="ir.ui.view">
+            <field name="name">custom.attribute.list</field>
+            <field name="model">custom.attribute</field>
             <field name="arch" type="xml">
                 <search string="Search Product Attributes">
                     <field name="name"/>
@@ -255,48 +148,18 @@
             </field>
         </record>
 
-        <record id="attribute_set_form_action" model="ir.actions.act_window">
-            <field name="name">Attribute Sets</field>
-            <field name="res_model">attribute.set</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form</field>
-            <field name="search_view_id" ref="view_attribute_set_search"/>
-            <field name="context">{"search_default_user_id":uid}</field>
-            <field name="help"></field>
-        </record>
-
-        <record id="attribute_group_form_action" model="ir.actions.act_window">
-            <field name="name">Attribute Groups</field>
-            <field name="res_model">attribute.group</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form</field>
-            <field name="search_view_id" ref="view_attribute_group_search"/>
-            <field name="context">{"search_default_user_id":uid}</field>
-            <field name="help"></field>
-        </record>
-
-        <record id="attribute_location_form_action" model="ir.actions.act_window">
-            <field name="name">Attribute Locations</field>
-            <field name="res_model">attribute.location</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form</field>
-            <field name="search_view_id" ref="view_attribute_location_search"/>
-            <field name="context">{"search_default_user_id":uid}</field>
-            <field name="help"></field>
-        </record>
-
-        <record id="product_attribute_form_action" model="ir.actions.act_window">
-            <field name="name">Product Attributes</field>
-            <field name="res_model">product.attribute</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form</field>
-            <field name="search_view_id" ref="view_product_attribute_search"/>
+        <record id="custom_attribute_form_action" model="ir.actions.act_window">
+            <field name="name">Custom Attributes</field>
+            <field name="res_model">custom.attribute</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="search_view_id" ref="view_custom_attribute_search"/>
             <field name="context">{"search_default_user_id":uid}</field>
             <field name="help"></field>
         </record>
 
         <record id="attribute_option_form_action" model="ir.actions.act_window">
-            <field name="name">Attribute Options</field>
+            <field name="name">Custom Attribute Options</field>
             <field name="res_model">attribute.option</field>
             <field name="view_type">form</field>
             <field name="view_mode">tree,form</field>
@@ -306,21 +169,24 @@
             <field name="help"></field>
         </record>
 
-         <menuitem
-             action="attribute_set_form_action" id="menu_attribute_set_action"
-             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="1"/>
+        <record id="attribute_group_form_action" model="ir.actions.act_window">
+            <field name="name">Custom Attribute Groups</field>
+            <field name="res_model">attribute.group</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="context">{"search_default_user_id":uid}</field>
+            <field name="help"></field>
+        </record>
+
          <menuitem
              action="attribute_group_form_action" id="menu_attribute_group_action"
-             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="2"/>
-         <menuitem
-             action="attribute_location_form_action" id="menu_attribute_location_action"
-             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="3"/>
-         <menuitem
-             action="product_attribute_form_action" id="menu_product_attribute_action"
-             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="4"/>
+             parent="base.next_id_9" sequence="2"/>
+         <menuitem
+             action="custom_attribute_form_action" id="menu_custom_attribute_action"
+             parent="base.next_id_9" sequence="4"/>
          <menuitem
              action="attribute_option_form_action" id="menu_attribute_option_action"
-             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="5"/>
+             parent="base.next_id_9" sequence="5"/>
 
     </data>
 </openerp>

=== modified file 'base_custom_attributes/security/ir.model.access.csv'
--- product_custom_attributes/security/ir.model.access.csv	2013-02-11 22:58:10 +0000
+++ base_custom_attributes/security/ir.model.access.csv	2013-03-02 00:14:23 +0000
@@ -1,16 +1,10 @@
 "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-"access_product_custom_attributes_attribute_set_salemanager","product_custom_attributes_attribute_set","product_custom_attributes.model_attribute_set","base.group_sale_manager",1,1,1,1
-"access_product_custom_attributes_attribute_group_salemanager","product_custom_attributes_attribute_group","product_custom_attributes.model_attribute_group","base.group_sale_manager",1,1,1,1
-"access_product_custom_attributes_attribute_location_salemanager","product_custom_attributes_attribute_location","product_custom_attributes.model_attribute_location","base.group_sale_manager",1,1,1,1
-"access_product_custom_attributes_product_attribute_salemanager","product_custom_attributes_product_attribute","product_custom_attributes.model_product_attribute","base.group_sale_manager",1,1,1,1
-"access_product_custom_attributes_attribute_option_salemanager","product_custom_attributes_attribute_option","product_custom_attributes.model_attribute_option","base.group_sale_manager",1,1,1,1
-"access_product_custom_attributes_attribute_set_manager","product_custom_attributes_attribute_set","product_custom_attributes.model_attribute_set","product.group_product_variant",1,1,1,1
-"access_product_custom_attributes_attribute_group_manager","product_custom_attributes_attribute_group","product_custom_attributes.model_attribute_group","product.group_product_variant",1,1,1,1
-"access_product_custom_attributes_attribute_location_manager","product_custom_attributes_attribute_location","product_custom_attributes.model_attribute_location","product.group_product_variant",1,1,1,1
-"access_product_custom_attributes_product_attribute_manager","product_custom_attributes_product_attribute","product_custom_attributes.model_product_attribute","product.group_product_variant",1,1,1,1
-"access_product_custom_attributes_attribute_option_manager","product_custom_attributes_attribute_option","product_custom_attributes.model_attribute_option","product.group_product_variant",1,1,1,1
-"access_product_custom_attributes_attribute_set_user","product_custom_attributes_attribute_set","product_custom_attributes.model_attribute_set","base.group_user",1,0,0,0
-"access_product_custom_attributes_attribute_group_user","product_custom_attributes_attribute_group","product_custom_attributes.model_attribute_group","base.group_user",1,0,0,0
-"access_product_custom_attributes_attribute_location_user","product_custom_attributes_attribute_location","product_custom_attributes.model_attribute_location","base.group_user",1,0,0,0
-"access_product_custom_attributes_product_attribute_user","product_custom_attributes_product_attribute","product_custom_attributes.model_product_attribute","base.group_user",1,0,0,0
-"access_product_custom_attributes_attribute_option_user","product_custom_attributes_attribute_option","product_custom_attributes.model_attribute_option","base.group_user",1,0,0,0
+"access_base_custom_attributes_attribute_group_salemanager","base_custom_attributes_attribute_group","base_custom_attributes.model_attribute_group","base.group_sale_manager",1,1,1,1
+"access_base_custom_attributes_product_attribute_salemanager","base_custom_attributes_product_attribute","base_custom_attributes.model_custom_attribute","base.group_sale_manager",1,1,1,1
+"access_base_custom_attributes_attribute_option_salemanager","base_custom_attributes_attribute_option","base_custom_attributes.model_attribute_option","base.group_sale_manager",1,1,1,1
+"access_base_custom_attributes_attribute_group_manager","base_custom_attributes_attribute_group","base_custom_attributes.model_attribute_group","product.group_product_variant",1,1,1,1
+"access_base_custom_attributes_product_attribute_manager","base_custom_attributes_product_attribute","base_custom_attributes.model_custom_attribute","product.group_product_variant",1,1,1,1
+"access_base_custom_attributes_attribute_option_manager","base_custom_attributes_attribute_option","base_custom_attributes.model_attribute_option","product.group_product_variant",1,1,1,1
+"access_base_custom_attributes_attribute_group_user","base_custom_attributes_attribute_group","base_custom_attributes.model_attribute_group","base.group_user",1,0,0,0
+"access_base_custom_attributes_product_attribute_user","base_custom_attributes_product_attribute","base_custom_attributes.model_custom_attribute","base.group_user",1,0,0,0
+"access_base_custom_attributes_attribute_option_user","base_custom_attributes_attribute_option","base_custom_attributes.model_attribute_option","base.group_user",1,0,0,0

=== added directory 'product_categ_attributes'
=== added file 'product_categ_attributes/__init__.py'
--- product_categ_attributes/__init__.py	1970-01-01 00:00:00 +0000
+++ product_categ_attributes/__init__.py	2013-03-02 00:14:23 +0000
@@ -0,0 +1,1 @@
+import product

=== added file 'product_categ_attributes/__openerp__.py'
--- product_categ_attributes/__openerp__.py	1970-01-01 00:00:00 +0000
+++ product_categ_attributes/__openerp__.py	2013-03-02 00:14:23 +0000
@@ -0,0 +1,42 @@
+# -*- encoding: utf-8 -*-
+###############################################################################
+#                                                                             #
+#   product_custom_attributes for OpenERP                                     #
+#   Copyright (C) 2013 Akretion Raphaël Valyi <raphael.valyi@xxxxxxxxxxxx>    #
+#                                                                             #
+#   This program is free software: you can redistribute it and/or modify      #
+#   it under the terms of the GNU Affero General Public License as            #
+#   published by the Free Software Foundation, either version 3 of the        #
+#   License, or (at your option) any later version.                           #
+#                                                                             #
+#   This program is distributed in the hope that it will be useful,           #
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
+#   GNU Affero General Public License for more details.                       #
+#                                                                             #
+#   You should have received a copy of the GNU Affero General Public License  #
+#   along with this program.  If not, see <http://www.gnu.org/licenses/>.     #
+#                                                                             #
+###############################################################################
+
+
+
+{
+    'name': 'product_categ_attributes',
+    'version': '0.1',
+    'category': 'Generic Modules/Others',
+    'license': 'AGPL-3',
+    'description': """Makes it possible to inherit product attributes from its categories
+    """,
+    'author': 'Akretion',
+    'website': 'http://www.akretion.com/',
+    'depends': ['product_custom_attributes', 'product_m2mcategories'],
+    'init_xml': [],
+    'update_xml': [
+        "product_view.xml"
+    ],
+    'demo_xml': [],
+    'installable': True,
+    'active': False,
+}
+

=== added file 'product_categ_attributes/product.py'
--- product_categ_attributes/product.py	1970-01-01 00:00:00 +0000
+++ product_categ_attributes/product.py	2013-03-02 00:14:23 +0000
@@ -0,0 +1,26 @@
+from openerp.osv.orm import Model
+from openerp.osv import fields
+from openerp.osv.osv import except_osv
+from openerp.tools.translate import _
+
+
+class product_category(Model):
+    _inherit = "product.category"
+    _columns = {
+        'attribute_group_ids': fields.many2many('attribute.group', 'categ_attr_grp_rel', 'categ_id', 'grp_id', 'Attribute Groups'),
+        }
+
+
+class product_product(Model):
+    _inherit = "product.product"
+
+    def _attr_grp_ids(self, cr, uid, ids, field_names, arg=None, context=None):
+        print "AAAAAAAAAAAAAA"
+        res = {}
+        for product in self.browse(cr, uid, ids, context=context):
+            grp_ids = []
+            for categ in product.categ_ids:
+                grp_ids += [grp.id for grp in categ.attribute_group_ids]
+            res[product.id] = grp_ids
+        return res
+

=== added file 'product_categ_attributes/product_view.xml'
--- product_categ_attributes/product_view.xml	1970-01-01 00:00:00 +0000
+++ product_categ_attributes/product_view.xml	2013-03-02 00:14:23 +0000
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<openerp>
+    <data>
+
+        <record model="ir.ui.view" id="product_categ_form_view">
+            <field name="name">product_categ_form_view</field>
+            <field name="model">product.category</field>
+            <field name="inherit_id" ref="product.product_category_form_view" />
+            <field name="arch" type="xml">
+                <field name="type" position="after">
+                    <field name="attribute_group_ids"/>
+                </field>
+            </field>
+        </record>
+
+        <record model="ir.ui.view" id="product_product_form_view_set_button">
+            <field name="name">attributes.product.normal.form</field>
+            <field name="model">product.product</field>
+            <field name="inherit_id" ref="product_custom_attributes.product_product_form_view_set_button" />
+            <field name="arch" type="xml">
+                <field name="attribute_set_id" position="replace">
+                </field>
+                <button name="open_attributes" position="replace">
+                    <button name="open_attributes" string="Open Attributes" type="object" icon="gtk-ok"/>
+                </button>
+            </field>
+        </record>
+
+        <record id="attribute_group_form_view" model="ir.ui.view">
+            <field name="name">attribute.group.form</field>
+            <field name="model">attribute.group</field>
+            <field name="inherit_id" ref="base_custom_attributes.attribute_group_form_view" />
+            <field name="arch" type="xml">
+                    <field name="attribute_set_id" position="replace">
+                    </field>
+            </field>
+        </record>
+
+    </data>
+</openerp>
+

=== added directory 'product_custom_attributes'
=== added file 'product_custom_attributes/__init__.py'
--- product_custom_attributes/__init__.py	1970-01-01 00:00:00 +0000
+++ product_custom_attributes/__init__.py	2013-03-02 00:14:23 +0000
@@ -0,0 +1,3 @@
+import product
+import custom_attributes
+import wizard

=== added file 'product_custom_attributes/__openerp__.py'
--- product_custom_attributes/__openerp__.py	1970-01-01 00:00:00 +0000
+++ product_custom_attributes/__openerp__.py	2013-03-02 00:14:23 +0000
@@ -0,0 +1,47 @@
+# -*- encoding: utf-8 -*-
+###############################################################################
+#                                                                             #
+#   product_custom_attributes for OpenERP                                      #
+#   Copyright (C) 2011 Akretion Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>  #
+#                                                                             #
+#   This program is free software: you can redistribute it and/or modify      #
+#   it under the terms of the GNU Affero General Public License as            #
+#   published by the Free Software Foundation, either version 3 of the        #
+#   License, or (at your option) any later version.                           #
+#                                                                             #
+#   This program is distributed in the hope that it will be useful,           #
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
+#   GNU Affero General Public License for more details.                       #
+#                                                                             #
+#   You should have received a copy of the GNU Affero General Public License  #
+#   along with this program.  If not, see <http://www.gnu.org/licenses/>.     #
+#                                                                             #
+###############################################################################
+
+
+
+{
+    'name': 'product_custom_attributes',
+    'version': '0.1',
+    'category': 'Generic Modules/Others',
+    'license': 'AGPL-3',
+    'description': """This module adds the possibility to easily create custom fields on products.
+Each product can be linked to an attribute set (like camera, fridge...).
+Each attribute has custom fields (for example, you don't need the same field for a frigde and a camera).
+In particular it's used by the Magento Magentoerpconnect module to match the EAV flexibility of Magento.
+    """,
+    'author': 'Akretion',
+    'website': 'http://www.akretion.com/',
+    'depends': ['product', 'base_custom_attributes'],
+    'init_xml': [],
+    'update_xml': [
+           'product_view.xml',
+           'custom_attributes_view.xml',
+           'wizard/open_product_by_attribute_set.xml',
+    ],
+    'demo_xml': [],
+    'installable': True,
+    'active': False,
+}
+

=== added file 'product_custom_attributes/custom_attributes.py'
--- product_custom_attributes/custom_attributes.py	1970-01-01 00:00:00 +0000
+++ product_custom_attributes/custom_attributes.py	2013-03-02 00:14:23 +0000
@@ -0,0 +1,67 @@
+# -*- encoding: utf-8 -*-
+###############################################################################
+#                                                                             #
+#   product_custom_attributes for OpenERP                                      #
+#   Copyright (C) 2011 Akretion Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>  #
+#                                                                             #
+#   This program is free software: you can redistribute it and/or modify      #
+#   it under the terms of the GNU Affero General Public License as            #
+#   published by the Free Software Foundation, either version 3 of the        #
+#   License, or (at your option) any later version.                           #
+#                                                                             #
+#   This program is distributed in the hope that it will be useful,           #
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
+#   GNU Affero General Public License for more details.                       #
+#                                                                             #
+#   You should have received a copy of the GNU Affero General Public License  #
+#   along with this program.  If not, see <http://www.gnu.org/licenses/>.     #
+#                                                                             #
+###############################################################################
+
+from openerp.osv.orm import Model
+from openerp.osv import fields
+from tools.translate import _
+
+
+class attribute_group(Model):
+    _inherit= "attribute.group"
+
+    _columns = {
+        'attribute_set_id': fields.many2one('attribute.set', 'Attribute Set'),
+        'attribute_ids': fields.one2many('attribute.location', 'attribute_group_id', 'Attributes'),
+    }
+
+    def create(self, cr, uid, vals, context=None):
+        for attribute in vals['attribute_ids']:
+            if vals.get('attribute_set_id') and attribute[2] and not attribute[2].get('attribute_set_id'):
+                attribute[2]['attribute_set_id'] = vals['attribute_set_id']
+        return super(attribute_group, self).create(cr, uid, vals, context)
+
+class attribute_set(Model):
+    _name = "attribute.set"
+    _description = "Attribute Set"
+    _columns = {
+        'name': fields.char('Name', size=128, required=True),
+        'attribute_group_ids': fields.one2many('attribute.group', 'attribute_set_id', 'Attribute Groups'),
+        }
+
+class attribute_location(Model):
+    _name = "attribute.location"
+    _description = "Attribute Location"
+    _order="sequence"
+    _inherits = {'custom.attribute': 'attribute_id'}
+
+
+    def _get_attribute_loc_from_group(self, cr, uid, ids, context=None):
+        return self.pool.get('attribute.location').search(cr, uid, [('attribute_group_id', 'in', ids)], context=context)
+
+    _columns = {
+        'attribute_id': fields.many2one('custom.attribute', 'Product Attribute', required=True, ondelete="cascade"),
+        'attribute_set_id': fields.related('attribute_group_id', 'attribute_set_id', type='many2one', relation='attribute.set', string='Attribute Set', readonly=True,
+store={
+            'attribute.group': (_get_attribute_loc_from_group, ['attribute_set_id'], 10),
+        }),
+        'attribute_group_id': fields.many2one('attribute.group', 'Attribute Group', required=True),
+        'sequence': fields.integer('Sequence'),
+        }

=== added file 'product_custom_attributes/custom_attributes_view.xml'
--- product_custom_attributes/custom_attributes_view.xml	1970-01-01 00:00:00 +0000
+++ product_custom_attributes/custom_attributes_view.xml	2013-03-02 00:14:23 +0000
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  base_custom_attributes for OpenERP
+  Copyright (C) 2011-2013 Akretion (http://www.akretion.com/)
+  @author: Benoît GUILLOT <benoit.guillot@xxxxxxxxxxxx>
+  The licence is in the file __openerp__.py
+-->
+
+<openerp>
+    <data>
+
+        <menuitem
+            id="menu_attribute_in_config_product" name="Attributes"
+            parent="product.prod_config_main" sequence="20"/>
+
+        <record id="attribute_set_form_view" model="ir.ui.view">
+            <field name="name">attribute.set.form</field>
+            <field name="model">attribute.set</field>
+            <field name="arch" type="xml">
+                <form string="Attribute Set">
+                    <field name="name" colspan="4"/>
+                    <field name="attribute_group_ids" colspan="4" >
+                        <form string="Attribute Groups">
+                            <field name="name" />
+                            <field name="sequence" />
+                            <field name="attribute_ids" colspan="4">
+                                <form string="Attribute Location">
+                                    <field name="attribute_id" />
+                                    <field name="sequence" />
+                                </form>
+                                <tree string="Attribute Location">
+                                    <field name="attribute_id" />
+                                </tree>
+                            </field>
+                        </form>
+                        <tree string="Attribute Groups">
+                            <field name="name" />
+                        </tree>
+                    </field>
+                </form>
+            </field>
+        </record>
+
+        <record id="attribute_location_form_view" model="ir.ui.view">
+            <field name="name">attribute.location.form</field>
+            <field name="model">attribute.location</field>
+            <field name="arch" type="xml">
+                <form string="Attribute Location">
+                    <field name="attribute_id" />
+                    <field name="sequence" />
+                    <field name="attribute_group_id" />
+                    <field name="attribute_set_id" />
+                </form>
+            </field>
+        </record>
+
+        <record id="attribute_set_tree_view" model="ir.ui.view">
+            <field name="name">attribute.set.tree</field>
+            <field name="model">attribute.set</field>
+            <field name="arch" type="xml">
+                <tree string="Attribute Set" >
+                    <field name="name" />
+                </tree>
+            </field>
+        </record>
+
+        <record id="attribute_group_tree_view" model="ir.ui.view">
+            <field name="model">attribute.group</field>
+            <field name="inherit_id" ref="base_custom_attributes.attribute_group_tree_view" />
+            <field name="arch" type="xml">
+                <field name="sequence" position="after">
+                    <field name="attribute_set_id" />
+                </field>
+            </field>
+        </record>
+
+        <record id="attribute_location_tree_view" model="ir.ui.view">
+            <field name="name">attribute.location.tree</field>
+            <field name="model">attribute.location</field>
+            <field name="arch" type="xml">
+                <tree string="Attribute Location">
+                    <field name="attribute_id" />
+                    <field name="sequence" />
+                    <field name="attribute_set_id" />
+                    <field name="attribute_group_id" />
+                </tree>
+            </field>
+        </record>
+
+        <record id="view_attribute_set_search" model="ir.ui.view">
+            <field name="name">attribute.set.list</field>
+            <field name="model">attribute.set</field>
+            <field name="arch" type="xml">
+                <search string="Search Attribute Sets">
+                    <field name="name"/>
+               </search>
+            </field>
+        </record>
+
+        <record id="view_attribute_group_search" model="ir.ui.view">
+            <field name="name">attribute.group.list</field>
+            <field name="model">attribute.group</field>
+            <field name="arch" type="xml">
+                <search string="Search Attribute Groups">
+                    <field name="name"/>
+                    <field name="attribute_set_id"/>
+               </search>
+            </field>
+        </record>
+
+        <record id="view_attribute_location_search" model="ir.ui.view">
+            <field name="name">attribute.location.list</field>
+            <field name="model">attribute.location</field>
+            <field name="arch" type="xml">
+                <search string="Search Attribute Locations">
+                    <field name="name"/>
+                    <field name="attribute_set_id"/>
+                    <field name="attribute_group_id"/>
+               </search>
+            </field>
+        </record>
+
+        <record id="attribute_set_form_action" model="ir.actions.act_window">
+            <field name="name">Attribute Sets</field>
+            <field name="res_model">attribute.set</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="search_view_id" ref="view_attribute_set_search"/>
+            <field name="context">{"search_default_user_id":uid}</field>
+            <field name="help"></field>
+        </record>
+
+        <record id="attribute_location_form_action" model="ir.actions.act_window">
+            <field name="name">Attribute Locations</field>
+            <field name="res_model">attribute.location</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="search_view_id" ref="view_attribute_location_search"/>
+            <field name="context">{"search_default_user_id":uid}</field>
+            <field name="help"></field>
+        </record>
+
+         <menuitem
+             action="attribute_set_form_action" id="menu_attribute_set_action"
+             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="1"/>
+         <menuitem
+             action="base_custom_attributes.attribute_group_form_action" id="menu_attribute_group_action"
+             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="2"/>
+         <menuitem
+             action="attribute_location_form_action" id="menu_attribute_location_action"
+             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="3"/>
+         <menuitem
+             action="base_custom_attributes.custom_attribute_form_action" id="menu_custom_attribute_action"
+             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="4"/>
+         <menuitem
+             action="base_custom_attributes.attribute_option_form_action" id="menu_attribute_option_action"
+             parent="product_custom_attributes.menu_attribute_in_config_product" sequence="5"/>
+
+    </data>
+</openerp>

=== renamed file 'product_custom_attributes/product.py' => 'product_custom_attributes/product.py'
--- product_custom_attributes/product.py	2013-03-02 00:14:23 +0000
+++ product_custom_attributes/product.py	2013-03-02 00:14:23 +0000
@@ -25,13 +25,6 @@
 from lxml import etree
 from tools.translate import _
 
-class product_template(Model):
-
-    _inherit = "product.template"
-
-    _columns = {
-        'attribute_custom_tmpl': fields.serialized('Custom Template Attributes'),
-    }
 
 class product_product(Model):
 
@@ -51,7 +44,6 @@
 
     _columns = {
         'attribute_set_id': fields.many2one('attribute.set', 'Attribute Set'),
-        'attribute_custom_variant': fields.serialized('Custom Variant Attributes'),
         'attribute_group_ids': fields.function(_attr_grp_ids, type='one2many',
         relation='attribute.group', string='Groups')
     }
@@ -90,39 +82,6 @@
     def save_and_close_product_attributes(self, cr, uid, ids, context=None):
         return {'type': 'ir.actions.act_window_close'}
 
-    def _build_attribute_field(self, cr, uid, page, attribute, context=None):
-        parent = page
-        kwargs = {'name': "%s" % attribute.name}
-        if attribute.ttype == 'many2many':
-            parent = etree.SubElement(page, 'group', colspan="2", col="4")
-            #FIXME the following isn't displayed in v7:
-            sep = etree.SubElement(parent, 'separator',
-                                    string="%s" % attribute.field_description, colspan="4")
-            kwargs['nolabel'] = "1"
-        if attribute.ttype in ['many2one', 'many2many']:
-            if attribute.relation_model_id:
-                if attribute.domain:
-                    kwargs['domain'] = attribute.domain
-                else:
-                    ids = [op.value_ref.id for op in attribute.option_ids]
-                    kwargs['domain'] = "[('id', 'in', %s)]" % ids
-            else:
-                kwargs['domain'] = "[('attribute_id', '=', %s)]" % attribute.attribute_id.id
-        field = etree.SubElement(parent, 'field', **kwargs)
-        return parent
-
-    def _build_attributes_notebook(self, cr, uid, attribute_group_ids, context=None):
-        notebook = etree.Element('notebook', name="attributes_notebook", colspan="4")
-        toupdate_fields = []
-        grp_obj = self.pool.get('attribute.group')
-        for group in grp_obj.browse(cr, uid, attribute_group_ids, context=context):
-            page = etree.SubElement(notebook, 'page', string=group.name.capitalize())
-            for attribute in group.attribute_ids:
-                if attribute.name not in toupdate_fields:
-                    toupdate_fields.append(attribute.name)
-                    self._build_attribute_field(cr, uid, page, attribute, context=context)
-        return notebook, toupdate_fields
-
     def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
         if context is None:
             context = {}
@@ -134,7 +93,7 @@
             if button:
                 button = button[0]
                 button.getparent().remove(button)
-            attributes_notebook, toupdate_fields = self._build_attributes_notebook(cr, uid, context['attribute_group_ids'], context=context)
+            attributes_notebook, toupdate_fields = self.pool.get('custom.attribute')._build_attributes_notebook(cr, uid, context['attribute_group_ids'], context=context)
             result['fields'].update(self.fields_get(cr, uid, toupdate_fields, context))
             if context.get('open_attributes'):
                 placeholder = eview.xpath("//separator[@string='attributes_placeholder']")[0]

=== renamed file 'product_custom_attributes/product_view.xml' => 'product_custom_attributes/product_view.xml'
=== renamed directory 'product_custom_attributes/wizard' => 'product_custom_attributes/wizard'

Follow ups