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

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

For more details, see:
https://code.launchpad.net/~akretion-team/openerp-product-attributes/polymorphic-relations/+merge/150725

Hello,

This is the round 2 of my proposed generalization of product_custom_attributes 
for OpenERP v7 and based on my experience building 2 product configurators for 
OpenERP.

So it does what the commits says:

[IMP][product_custom_attributes] generalization of the m2o and m2m custom 
options: you can still define a selection of attribute.option (names) BUT now 
you can also use the relation_model_id and the 'Change Options' button to 
define a selection of existing references to ANY kind of OpenERP record you 
want! You can also simply define a domain of valid records of a given model if 
you prefer. This will make this module a powerful reusable component for 
OpenERP configurators such as a product configurator.

[IMP][product_custom_attributes] when an attribute is relational and has a 
specific relation_model_id defined, then it's now created with the according 
ttype.relation. Further, attribute edition now has its domain properly tuned if 
it's such a relational field. Notice that the old behavior is totally preserved.

Notice that if that is accepted, I plan a very simple round 3 that will just 
consist in extracting a part of that module into a lower level 
custom_attributes mixin module that could be injected in any OpenERP object, 
not just products.

I also plan a little demo to show the power of this change. I know that it's 
not a trivial merge. However, I insist that the former behavior is largely 
preserved (if not totally) while accepting such change could make this module 
used in many more situations by much more people. Or said differently: it's 
makes it a little more complex but then more people will be maintaining it and 
IMHO this is totally worth the few additional lines of this merge.

Be the LGTM Gods with me!
-- 
https://code.launchpad.net/~akretion-team/openerp-product-attributes/polymorphic-relations/+merge/150725
Your team OpenERP Community is subscribed to branch 
lp:openerp-product-attributes.
=== modified file 'product_custom_attributes/product.py'
--- product_custom_attributes/product.py	2013-02-15 04:36:45 +0000
+++ product_custom_attributes/product.py	2013-02-27 05:28:18 +0000
@@ -95,11 +95,19 @@
         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']:
-            kwargs['domain'] = "[('attribute_id', '=', %s)]" % attribute.attribute_id.id
+            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
 

=== modified file 'product_custom_attributes/product_attribute.py'
--- product_custom_attributes/product_attribute.py	2013-02-06 14:37:21 +0000
+++ product_custom_attributes/product_attribute.py	2013-02-27 05:28:18 +0000
@@ -20,27 +20,109 @@
 ###############################################################################
 
 from openerp.osv.orm import Model
+from openerp.osv import osv
 from openerp.osv import fields
 from openerp.osv.osv import except_osv
+from lxml import etree
 from openerp.tools.translate import _
 from unidecode import unidecode # Debian package python-unidecode
 
+
 class attribute_option(Model):
     _name = "attribute.option"
     _description = "Attribute Option"
     _order="sequence"
 
+    _rec_name = 'value_ref' #FIXME add validation constraint to enforce model homogeneity
+
     _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),
         'sequence': fields.integer('Sequence'),
     }
 
+    def name_change(self, cr, uid, ids, name, relation_model_id, context=None):
+        if relation_model_id:
+            warning = {'title': _('Error!'), 'message': _("Use the 'Change Options' button instead to select appropriate model references'")}
+            return {"value": {"name": False}, "warning": warning}
+        else:
+            return True
+
+class attribute_option_wizard(osv.osv_memory):
+    _name = "attribute.option.wizard"
+    _rec_name = 'attribute_id'
+
+    _columns = {
+        'attribute_id': fields.many2one('product.attribute', 'Product Attribute', required=True),
+    }
+
+    _defaults = {
+        'attribute_id': lambda self, cr, uid, context: context.get('attribute_id', False)
+    }
+
+    def validate(self, cr, uid, ids, context=None):
+        return True
+
+    def create(self, cr, uid, vals, context=None):
+        attr_obj = self.pool.get("product.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")
+        opt_obj.unlink(cr, uid, op_ids)
+        for op_id in (vals.get("option_ids") and vals['option_ids'][0][2] or []):
+            model = attr.relation_model_id.model
+            name = self.pool.get(model).name_get(cr, uid, [op_id], context)[0][1]
+            opt_obj.create(cr, uid, {
+                'attribute_id': vals['attribute_id'],
+                'name': name,
+                'value_ref': "%s,%s" % (attr.relation_model_id.model, op_id)
+            })
+        res = super(attribute_option_wizard, self).create(cr, uid, vals, context)
+        return res
+
+    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")
+            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': {
+                            'domain': [],
+                            'string': "Options",
+                            'type': 'many2many',
+                            'relation': relation,
+                            'required': True,
+                            }
+                        })
+            eview = etree.fromstring(res['arch'])
+            options = etree.Element('field', name='option_ids', colspan='6')
+            placeholder = eview.xpath("//separator[@string='options_placeholder']")[0]
+            placeholder.getparent().replace(placeholder, options)
+            res['arch'] = etree.tostring(eview, pretty_print=True)
+        return res
+
 
 class product_attribute(Model):
     _name = "product.attribute"
     _description = "Product Attribute"
     _inherits = {'ir.model.fields': 'field_id'}
+
+    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]}}
+
+    def button_add_options(self, cr, uid, ids, context=None):
+        return {
+            'context': "{'attribute_id': %s}" % (ids[0]),
+            'name': _('Options Wizard'),
+            'view_type': 'form',
+            'view_mode': 'form',
+            'res_model': 'attribute.option.wizard',
+            'type': 'ir.actions.act_window',
+            'target': 'new',
+        }
+
     _columns = {
         'field_id': fields.many2one('ir.model.fields', 'Ir Model Fields', required=True, ondelete="cascade"),
         'attribute_type': fields.selection([('char','Char'),
@@ -62,9 +144,15 @@
                                      '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'),
+        'domain': fields.char('Domain', size=256),
         }
 
     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']
+        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'
@@ -75,10 +163,10 @@
             vals['serialization_field_id'] = self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', serial_name)], context=context)[0]
         if vals['attribute_type'] == 'select':
             vals['ttype'] = 'many2one'
-            vals['relation'] = 'attribute.option'
+            vals['relation'] = relation
         elif vals['attribute_type'] == 'multiselect':
             vals['ttype'] = 'many2many'
-            vals['relation'] = 'attribute.option'
+            vals['relation'] = relation
             if not vals.get('serialized'):
                 raise except_osv(_('Create Error'), _("The field serialized should be ticked for a multiselect field !"))
         else:

=== modified file 'product_custom_attributes/product_attribute_view.xml'
--- product_custom_attributes/product_attribute_view.xml	2013-02-11 22:58:10 +0000
+++ product_custom_attributes/product_attribute_view.xml	2013-02-27 05:28:18 +0000
@@ -88,23 +88,43 @@
                     <field name="size" attrs="{'invisible':[('attribute_type', '!=', 'char')]}"/>
                     <field name="translate" attrs="{'invisible':[('attribute_type', 'not in', ('char', 'text'))]}"/>
                     <newline />
-                    <field name="option_ids" colspan="4" attrs="{'invisible':[('attribute_type', 'not in', ['select', 'multiselect'])]}" widget="one2many_list" nolabel="1">
-                        <tree string="Attribute Options" editable="top" >
-                            <field name="sequence" />
-                            <field name="name" />
+                    <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"/>
+                    <field name="option_ids" colspan="4" nolabel="1">
+                        <tree string="Attribute Options" editable="top">
+                            <field name="sequence" invisible="1"/>
+                            <field name="name" on_change="name_change(name, parent.relation_model_id, context)"/>
                         </tree>
                     </field>
+                    </group>
+                    </group>
                     <field name="create_date" invisible="1"/>
                 </form>
             </field>
         </record>
 
+        <record id="attribute_option_wizard_form_view" model="ir.ui.view">
+            <field name="name">attribute.option.wizard</field>
+            <field name="model">attribute.option.wizard</field>
+            <field name="arch" type="xml">
+                <form string="Options Wizard" col="6">
+                    <field name="attribute_id" invisible="1" colspan="2"/>
+                    <separator string="options_placeholder"/>
+                    <button special="cancel" string="Cancel" icon="gtk-cancel"/>
+                    <button name="validate" string="Validate" type="object" icon="gtk-convert"/>
+                </form>
+            </field>
+        </record>
+
         <record id="attribute_option_form_view" model="ir.ui.view">
             <field name="name">attribute.option.form</field>
             <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"/>
                 </form>
@@ -163,7 +183,7 @@
             <field eval="1" name="priority"/>
             <field name="arch" type="xml">
                 <tree string="Attribute Option">
-                    <field name="name" />
+                    <field name="value_ref" />
                 </tree>
             </field>
         </record>
@@ -175,7 +195,7 @@
             <field name="arch" type="xml">
                 <tree string="Attribute Option">
                     <field name="sequence" />
-                    <field name="name" />
+                    <field name="value_ref" />
                     <field name="attribute_id" />
                 </tree>
             </field>
@@ -229,7 +249,7 @@
             <field name="model">attribute.option</field>
             <field name="arch" type="xml">
                 <search string="Search Attribute Options">
-                    <field name="name" />
+                    <field name="value_ref" />
                     <field name="attribute_id"/>
                </search>
             </field>

_______________________________________________
Mailing list: https://launchpad.net/~openerp-community
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-community
More help   : https://help.launchpad.net/ListHelp

Reply via email to