Browse Source

Support main types of fields

pull/213/head
Guewen Baconnier 10 years ago
committed by Julien Thewys
parent
commit
f0ca39b1ab
  1. 91
      partner_revision/models/res_partner.py
  2. 93
      partner_revision/models/res_partner_revision.py
  3. 30
      partner_revision/tests/common.py
  4. 170
      partner_revision/tests/test_revision_field_type.py
  5. 5
      partner_revision/views/res_partner_revision_views.xml

91
partner_revision/models/res_partner.py

@ -27,9 +27,70 @@ class ResPartner(models.Model):
@api.multi
def write(self, values):
for record in self:
values = record._add_revision(values)
return super(ResPartner, self).write(values)
if self.env.context.get('__no_revision'):
return super(ResPartner, self).write(values)
else:
for record in self:
values = record._add_revision(values)
super(ResPartner, record).write(values)
return True
@api.multi
def _has_field_changed(self, field, value):
self.ensure_one()
field_def = self._fields[field]
return field_def.convert_to_write(self[field]) != value
@api.multi
def convert_field_for_revision(self, field, value):
field_def = self._fields[field]
if field_def.type == 'many2one':
# store as 'reference'
comodel = field_def.comodel_name
return "%s,%s" % (comodel, value) if value else False
else:
return value
@api.multi
def _prepare_revision_change(self, rule, field, value):
""" Prepare data for a revision change
It returns a dict of the values to write on the revision change
and a boolean that indicates if the value should be popped out
of the values to write on the model.
:returns: dict of values, boolean
"""
field_def = self._fields[field]
# get a ready to write value for the type of the field,
# for instance takes '.id' from a many2one's record (the
# new value is already a value as expected for the
# write)
current_value = field_def.convert_to_write(self[field])
# get values ready to write as expected by the revision
# (for instance, a many2one is written in a reference
# field)
current_value = self.convert_field_for_revision(field,
current_value)
new_value = self.convert_field_for_revision(field, value)
change = {
'current_value': current_value,
'new_value': new_value,
'field_id': rule.field_id.id,
}
pop_value = False
if not self.env.context.get('__revision_rules'):
# by default always write on partner
change['state'] = 'done'
elif rule.default_behavior == 'auto':
change['state'] = 'done'
elif rule.default_behavior == 'validate':
change['state'] = 'draft'
pop_value = True # change to apply manually
elif rule.default_behavior == 'never':
change['state'] = 'cancel'
pop_value = True # change never applied
return change, pop_value
@api.multi
def _add_revision(self, values):
@ -57,25 +118,13 @@ class ResPartner(models.Model):
if not rule:
continue
if field in values:
if self[field] == values[field]:
# TODO handle relations, types
if not self._has_field_changed(field, values[field]):
continue
change = {
'current_value': self[field],
'new_value': values[field],
'field_id': rule.field_id.id,
}
if not self.env.context.get('__revision_rules'):
# by default always write on partner
change['state'] = 'done'
elif rule.default_behavior == 'auto':
change['state'] = 'done'
elif rule.default_behavior == 'validate':
change['state'] = 'draft'
write_values.pop(field) # change to apply manually
elif rule.default_behavior == 'never':
change['state'] = 'cancel'
write_values.pop(field) # change never applied
change, pop_value = self._prepare_revision_change(
rule, field, values[field]
)
if pop_value:
write_values.pop(field)
changes.append(change)
if changes:
self.env['res.partner.revision'].create({

93
partner_revision/models/res_partner_revision.py

@ -45,7 +45,7 @@ class ResPartnerRevision(models.Model):
class ResPartnerRevisionChange(models.Model):
_name = 'res.partner.revision.change'
_description = 'Partner Revision Change'
_rec_name = 'new_value'
_rec_name = 'field_id'
revision_id = fields.Many2one(comodel_name='res.partner.revision',
required=True,
@ -54,9 +54,27 @@ class ResPartnerRevisionChange(models.Model):
field_id = fields.Many2one(comodel_name='ir.model.fields',
string='Field',
required=True)
# TODO: different types of fields
current_value = fields.Char('Current')
new_value = fields.Char('New')
current_value_char = fields.Char(string='Current')
current_value_date = fields.Date(string='Current')
current_value_datetime = fields.Datetime(string='Current')
current_value_float = fields.Float(string='Current')
current_value_integer = fields.Integer(string='Current')
current_value_text = fields.Text(string='Current')
current_value_boolean = fields.Boolean(string='Current')
current_value_reference = fields.Reference(string='Current',
selection='_reference_models')
new_value_char = fields.Char(string='New')
new_value_date = fields.Date(string='New')
new_value_datetime = fields.Datetime(string='New')
new_value_float = fields.Float(string='New')
new_value_integer = fields.Integer(string='New')
new_value_text = fields.Text(string='New')
new_value_boolean = fields.Boolean(string='New')
new_value_reference = fields.Reference(string='New',
selection='_reference_models')
state = fields.Selection(
selection=[('draft', 'Waiting'),
('done', 'Accepted'),
@ -66,10 +84,75 @@ class ResPartnerRevisionChange(models.Model):
default='draft',
)
@api.model
def _reference_models(self):
models = self.env['ir.model'].search([])
return [(model.model, model.name) for model in models]
_type_to_field = {
'char': 'char',
'date': 'date',
'datetime': 'datetime',
'float': 'float',
'integer': 'integer',
'text': 'text',
'boolean': 'boolean',
'many2one': 'reference',
'selection': 'char',
}
@api.model
def create(self, vals):
vals = vals.copy()
field = self.env['ir.model.fields'].browse(vals.get('field_id'))
if 'current_value' in vals:
current_value = vals.pop('current_value')
if field:
current_field_name = self.get_field_for_type(field, 'current')
vals[current_field_name] = current_value
if 'new_value' in vals:
new_value = vals.pop('new_value')
if field:
new_field_name = self.get_field_for_type(field, 'new')
vals[new_field_name] = new_value
return super(ResPartnerRevisionChange, self).create(vals)
@api.model
def get_field_for_type(self, field, current_or_new):
assert current_or_new in ('new', 'current')
field_type = self._type_to_field.get(field.ttype)
if not field_type:
# TODO: prevent to create unsupported types from the views
raise NotImplementedError(
'field type %s is not supported' % field_type
)
return '%s_value_%s' % (current_or_new, field_type)
@api.multi
def get_current_value(self):
self.ensure_one()
field_name = self.get_field_for_type(self.field_id, 'current')
return self[field_name]
@api.multi
def get_new_value(self):
self.ensure_one()
field_name = self.get_field_for_type(self.field_id, 'new')
return self[field_name]
@api.multi
def _convert_value_for_write(self, value):
model = self.env[self.field_id.model_id.model]
model_field_def = model._fields[self.field_id.name]
return model_field_def.convert_to_write(value)
@api.multi
def apply(self):
for change in self:
if change.state in ('cancel', 'done'):
continue
partner = change.revision_id.partner_id
partner.write({change.field_id.name: change.new_value})
value_for_write = change._convert_value_for_write(
change.get_new_value()
)
partner.write({change.field_id.name: value_for_write})

30
partner_revision/tests/common.py

@ -44,7 +44,9 @@ class RevisionMixin(object):
missing = []
for expected_change in expected_changes:
for change in changes:
if (change.field_id, change.current_value, change.new_value,
if (change.field_id,
change.get_current_value(),
change.get_new_value(),
change.state) == expected_change:
changes -= change
break
@ -58,8 +60,10 @@ class RevisionMixin(object):
for change in changes:
message += ("+ field: '%s', current_value: '%s', "
"new_value: '%s', state: '%s'\n" %
(change.field_id.name, change.current_value,
change.new_value, change.state))
(change.field_id.name,
change.get_current_value(),
change.get_new_value(),
change.state))
if message:
raise AssertionError('Changes do not match\n\n:%s' % message)
@ -70,14 +74,22 @@ class RevisionMixin(object):
:param changes: list of changes [(field, new value, state)]
:returns: 'res.partner.revision' record
"""
change_values = [
(0, 0, {
get_field = self.env['res.partner.revision.change'].get_field_for_type
convert = self.env['res.partner'].convert_field_for_revision
change_values = []
for field, value, state in changes:
field_def = self.env['res.partner']._fields[field.name]
current_value = field_def.convert_to_write(partner[field.name])
current_value = convert(field.name, current_value)
change = {
'field_id': field.id,
'current_value': partner[field.name],
'new_value': value,
# write in the field of the appropriate type for the
# current field (char, many2one, ...)
get_field(field, 'current'): current_value,
get_field(field, 'new'): value,
'state': state,
}) for field, value, state in changes
]
}
change_values.append((0, 0, change))
values = {
'partner_id': partner.id,
'change_ids': change_values,

170
partner_revision/tests/test_revision_field_type.py

@ -47,7 +47,10 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
)
for field_type, field in fields:
attr_name = 'field_%s' % field_type
field_record = self.env.ref('base.field_res_partner_%s' % field)
field_record = self.env['ir.model.fields'].search([
('model', '=', 'res.partner'),
('name', '=', field),
])
# set attribute such as 'self.field_char' is a
# ir.model.fields record of the field res_partner.ref
setattr(self, attr_name, field_record)
@ -65,14 +68,140 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
'street': 'Original Street',
})
def test_char(self):
def test_new_revision_char(self):
""" Add a new revision on a Char field """
self.partner.with_context(__revision_rules=True).write({
self.field_char.name: 'New value',
})
self.assert_revision(
self.partner,
[(self.field_char, self.partner[self.field_char.name],
'New value', 'draft'),
]
)
def test_new_revision_text(self):
""" Add a new revision on a Text field """
self.partner.with_context(__revision_rules=True).write({
self.field_text.name: 'New comment\non 2 lines',
})
self.assert_revision(
self.partner,
[(self.field_text, self.partner[self.field_text.name],
'New comment\non 2 lines', 'draft'),
]
)
def test_new_revision_boolean(self):
""" Add a new revision on a Boolean field """
# ensure the revision has to change the value
self.partner.with_context(__no_revision=True).write({
self.field_boolean.name: False,
})
self.partner.with_context(__revision_rules=True).write({
self.field_boolean.name: True,
})
self.assert_revision(
self.partner,
[(self.field_boolean, self.partner[self.field_boolean.name],
True, 'draft'),
]
)
def test_new_revision_date(self):
""" Add a new revision on a Date field """
self.partner.with_context(__revision_rules=True).write({
self.field_date.name: '2015-09-15',
})
self.assert_revision(
self.partner,
[(self.field_date, self.partner[self.field_date.name],
'2015-09-15', 'draft'),
]
)
def test_new_revision_integer(self):
""" Add a new revision on a Integer field """
self.partner.with_context(__revision_rules=True).write({
self.field_integer.name: 42,
})
self.assert_revision(
self.partner,
[(self.field_integer, self.partner[self.field_integer.name],
42, 'draft'),
]
)
def test_new_revision_float(self):
""" Add a new revision on a Float field """
self.partner.with_context(__revision_rules=True).write({
self.field_float.name: 3.1415,
})
self.assert_revision(
self.partner,
[(self.field_float, self.partner[self.field_float.name],
3.1415, 'draft'),
]
)
def test_new_revision_selection(self):
""" Add a new revision on a Selection field """
self.partner.with_context(__revision_rules=True).write({
self.field_selection.name: 'delivery',
})
self.assert_revision(
self.partner,
[(self.field_selection, self.partner[self.field_selection.name],
'delivery', 'draft'),
]
)
def test_new_revision_many2one(self):
""" Add a new revision on a Many2one field """
self.partner.with_context(__no_revision=True).write({
self.field_many2one.name: self.env.ref('base.fr').id,
})
self.partner.with_context(__revision_rules=True).write({
self.field_many2one.name: self.env.ref('base.ch').id,
})
self.assert_revision(
self.partner,
[(self.field_many2one, self.partner[self.field_many2one.name],
self.env.ref('base.ch'), 'draft'),
]
)
def test_new_revision_many2many(self):
""" Add a new revision on a Many2many field is not supported """
with self.assertRaises(NotImplementedError):
self.partner.with_context(__revision_rules=True).write({
self.field_many2many.name: [self.env.ref('base.ch').id],
})
def test_new_revision_one2many(self):
""" Add a new revision on a One2many field is not supported """
with self.assertRaises(NotImplementedError):
self.partner.with_context(__revision_rules=True).write({
self.field_one2many.name: [self.env.ref('base.user_root').id],
})
def test_new_revision_binary(self):
""" Add a new revision on a Binary field is not supported """
with self.assertRaises(NotImplementedError):
self.partner.with_context(__revision_rules=True).write({
self.field_binary.name: '',
})
def test_apply_char(self):
""" Apply a change on a Char field """
changes = [(self.field_char, 'New Ref', 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
self.assertEqual(self.partner[self.field_char.name], 'New Ref')
def test_text(self):
def test_apply_text(self):
""" Apply a change on a Text field """
changes = [(self.field_text, 'New comment\non 2 lines', 'draft')]
revision = self._create_revision(self.partner, changes)
@ -80,7 +209,7 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
self.assertEqual(self.partner[self.field_text.name],
'New comment\non 2 lines')
def test_boolean(self):
def test_apply_boolean(self):
""" Apply a change on a Boolean field """
# ensure the revision has to change the value
self.partner.write({self.field_boolean.name: False})
@ -95,7 +224,7 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
revision.change_ids.apply()
self.assertEqual(self.partner[self.field_boolean.name], False)
def test_date(self):
def test_apply_date(self):
""" Apply a change on a Date field """
changes = [(self.field_date, '2015-09-15', 'draft')]
revision = self._create_revision(self.partner, changes)
@ -103,21 +232,21 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
self.assertAlmostEqual(self.partner[self.field_date.name],
'2015-09-15')
def test_integer(self):
def test_apply_integer(self):
""" Apply a change on a Integer field """
changes = [(self.field_integer, 42, 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
self.assertAlmostEqual(self.partner[self.field_integer.name], 42)
def test_float(self):
def test_apply_float(self):
""" Apply a change on a Float field """
changes = [(self.field_float, 52.47, 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
self.assertAlmostEqual(self.partner[self.field_float.name], 52.47)
def test_selection(self):
def test_apply_selection(self):
""" Apply a change on a Selection field """
changes = [(self.field_selection, 'delivery', 'draft')]
revision = self._create_revision(self.partner, changes)
@ -125,26 +254,31 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
self.assertAlmostEqual(self.partner[self.field_selection.name],
'delivery')
def test_many2one(self):
def test_apply_many2one(self):
""" Apply a change on a Many2one field """
self.s = True
self.partner.with_context(__no_revision=True).write({
self.field_many2one.name: self.env.ref('base.fr').id,
})
changes = [(self.field_many2one,
self.env.ref('base.ch').id,
'res.country,%d' % self.env.ref('base.ch').id,
'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
self.assertAlmostEqual(self.partner[self.field_many2one.name],
self.env.ref('base.ch').id)
self.assertEqual(self.partner[self.field_many2one.name],
self.env.ref('base.ch'))
def test_many2many(self):
""" Apply a change on a Many2many field """
def test_apply_many2many(self):
""" Apply a change on a Many2many field is not supported """
changes = [(self.field_many2many,
self.env.ref('base.ch').id,
'draft')]
with self.assertRaises(NotImplementedError):
self._create_revision(self.partner, changes)
def test_one2many(self):
""" Apply a change on a One2many field """
def test_apply_one2many(self):
""" Apply a change on a One2many field is not supported """
changes = [(self.field_one2many,
[self.env.ref('base.user_root').id,
self.env.ref('base.user_demo').id,
@ -153,8 +287,8 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
with self.assertRaises(NotImplementedError):
self._create_revision(self.partner, changes)
def test_binary(self):
""" Apply a change on a Binary field """
def test_apply_binary(self):
""" Apply a change on a Binary field is not supported """
changes = [(self.field_one2many, '', 'draft')]
with self.assertRaises(NotImplementedError):
self._create_revision(self.partner, changes)

5
partner_revision/views/res_partner_revision_views.xml

@ -29,8 +29,9 @@
<field name="change_ids" nolabel="1">
<tree>
<field name="field_id" context="{'no_open': true}"/>
<field name="current_value"/>
<field name="new_value"/>
<!-- TODO show according to field ttype -->
<field name="current_value_char"/>
<field name="new_value_char"/>
<field name="state"/>
</tree>
</field>

Loading…
Cancel
Save