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