Browse Source

Rename 'revision' to 'changeset'

Because the term seems more adapted to what this module does: it does not
create revisions as one could expect with the full content of the partner,
instead it proposes changesets on a number of fields.

The renaming of the files follows in a second commit.
pull/208/head
Guewen Baconnier 9 years ago
parent
commit
e3c299828e
  1. 40
      partner_revision/README.rst
  2. 2
      partner_revision/__openerp__.py
  3. 30
      partner_revision/models/res_partner.py
  4. 98
      partner_revision/models/res_partner_revision.py
  5. 12
      partner_revision/models/revision_field_rule.py
  6. 18
      partner_revision/security/ir.model.access.csv
  7. 14
      partner_revision/security/security.xml
  8. 32
      partner_revision/tests/common.py
  9. 146
      partner_revision/tests/test_revision_field_type.py
  10. 150
      partner_revision/tests/test_revision_flow.py
  11. 34
      partner_revision/tests/test_revision_origin.py
  12. 6
      partner_revision/views/menu.xml
  13. 51
      partner_revision/views/res_partner_revision_views.xml
  14. 16
      partner_revision/views/res_partner_views.xml
  15. 43
      partner_revision/views/revision_field_rule_views.xml

40
partner_revision/README.rst

@ -1,6 +1,6 @@
=================
Partner Revisions
=================
==================
Partner Changesets
==================
Configuration Configuration
============= =============
@ -8,24 +8,24 @@ Configuration
Access Rights Access Rights
------------- -------------
The revisions rules must be edited by users with the group ``Revision
Configuration``. The revisions can be applied or canceled only by users
with the group ``Revisions Validations``
The changesets rules must be edited by users with the group ``Changesets
Configuration``. The changesets can be applied or canceled only by users
with the group ``Changesets Validations``
Revision Rules
--------------
Changesets Rules
----------------
The revision rules can be configured in ``Sales > Configuration >
Partner Revisions > Revision Fields Rules``. For each partner field, an
The changesets rules can be configured in ``Sales > Configuration >
Partner Changesets > Fields Rules``. For each partner field, an
action can be defined: action can be defined:
* Auto: the changes made on this field are always applied * Auto: the changes made on this field are always applied
* Validate: the changes made on this field must be manually confirmed by * Validate: the changes made on this field must be manually confirmed by
a 'Revision User' user
a 'Changesets User' user
* Never: the changes made on this field are always refused * Never: the changes made on this field are always refused
In any case, all the changes made by the users are always applied In any case, all the changes made by the users are always applied
directly on the users, but a 'validated' revision is created for the
directly on the users, but a 'validated' changeset is created for the
history. history.
The supported fields are: The supported fields are:
@ -45,28 +45,28 @@ Usage
General case General case
------------ ------------
When users modify the partners, new 'validated' revisions are created so
there is nothing to do. Addons wanting to create revisions which need a
validation should pass the key ``__revision_rules`` in the context when
When users modify the partners, new 'validated' changeset are created so
there is nothing to do. Addons wanting to create changeset which need a
validation should pass the key ``_changeset_rules`` in the context when
they write on the partner. they write on the partner.
Finding changesets Finding changesets
------------------ ------------------
A menu shows all the changesets in ``Sales > Configuration > Partner A menu shows all the changesets in ``Sales > Configuration > Partner
Revisions > Partner Revision``.
Changesets > Changesets``.
However, it is more convenient to access them directly from the However, it is more convenient to access them directly from the
partners. Pending revisions can be accessed directly from the top right
partners. Pending changesets can be accessed directly from the top right
of the partners' view. A new filter on the partners shows the partners of the partners' view. A new filter on the partners shows the partners
having at least one pending revision.
having at least one pending changeset.
Handling changesets Handling changesets
------------------- -------------------
A revision shows the list of the changes made on a partner. Some of the
A changeset shows the list of the changes made on a partner. Some of the
changes may be 'Pending', some 'Accepted' or 'Rejected' according to the changes may be 'Pending', some 'Accepted' or 'Rejected' according to the
revision rules. The only changes that need an action from the user are
changeset rules. The only changes that need an action from the user are
'Pending' changes. When a change is accepted, the value is written on 'Pending' changes. When a change is accepted, the value is written on
the user. the user.

2
partner_revision/__openerp__.py

@ -19,7 +19,7 @@
# #
# #
{'name': 'Partner Revisions',
{'name': 'Partner Changesets',
'version': '1.0', 'version': '1.0',
'author': 'Camptocamp', 'author': 'Camptocamp',
'license': 'AGPL-3', 'license': 'AGPL-3',

30
partner_revision/models/res_partner.py

@ -25,40 +25,40 @@ from openerp import models, fields, api
class ResPartner(models.Model): class ResPartner(models.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
revision_ids = fields.One2many(comodel_name='res.partner.revision',
changeset_ids = fields.One2many(comodel_name='res.partner.changeset',
inverse_name='partner_id', inverse_name='partner_id',
string='Revisions',
string='Changesets',
readonly=True) readonly=True)
count_pending_revisions = fields.Integer(
string='Pending Revisions',
compute='_count_pending_revisions',
search='_search_count_pending_revisions')
count_pending_changesets = fields.Integer(
string='Pending Changesets',
compute='_count_pending_changesets',
search='_search_count_pending_changesets')
@api.one @api.one
@api.depends('revision_ids', 'revision_ids.state')
def _count_pending_revisions(self):
revisions = self.revision_ids.filtered(
@api.depends('changeset_ids', 'changeset_ids.state')
def _count_pending_changesets(self):
changesets = self.changeset_ids.filtered(
lambda rev: rev.state == 'draft' and rev.partner_id == self lambda rev: rev.state == 'draft' and rev.partner_id == self
) )
self.count_pending_revisions = len(revisions)
self.count_pending_changesets = len(changesets)
@api.multi @api.multi
def write(self, values): def write(self, values):
if self.env.context.get('__no_revision'):
if self.env.context.get('__no_changeset'):
return super(ResPartner, self).write(values) return super(ResPartner, self).write(values)
else: else:
revision_model = self.env['res.partner.revision']
changeset_model = self.env['res.partner.changeset']
for record in self: for record in self:
local_values = revision_model.add_revision(record, values)
local_values = changeset_model.add_changeset(record, values)
super(ResPartner, record).write(local_values) super(ResPartner, record).write(local_values)
return True return True
def _search_count_pending_revisions(self, operator, value):
def _search_count_pending_changesets(self, operator, value):
if operator not in ('=', '!=', '<', '<=', '>', '>=', 'in', 'not in'): if operator not in ('=', '!=', '<', '<=', '>', '>=', 'in', 'not in'):
return [] return []
query = ("SELECT p.id " query = ("SELECT p.id "
"FROM res_partner p " "FROM res_partner p "
"INNER JOIN res_partner_revision r ON r.partner_id = p.id "
"INNER JOIN res_partner_changeset r ON r.partner_id = p.id "
"WHERE r.state = 'draft' " "WHERE r.state = 'draft' "
"GROUP BY p.id " "GROUP BY p.id "
"HAVING COUNT(r.id) %s %%s ") % operator "HAVING COUNT(r.id) %s %%s ") % operator

98
partner_revision/models/res_partner_revision.py

@ -27,13 +27,13 @@ from openerp import models, fields, api, exceptions, _
from openerp.osv.orm import setup_modifiers from openerp.osv.orm import setup_modifiers
# sentinel object to be sure that no empty value was passed to # sentinel object to be sure that no empty value was passed to
# ResPartnerRevisionChange._value_for_revision
# ResPartnerChangesetChange._value_for_changeset
_NO_VALUE = object() _NO_VALUE = object()
class ResPartnerRevision(models.Model):
_name = 'res.partner.revision'
_description = 'Partner Revision'
class ResPartnerChangeset(models.Model):
_name = 'res.partner.changeset'
_description = 'Partner Changeset'
_order = 'date desc' _order = 'date desc'
_rec_name = 'date' _rec_name = 'date'
@ -42,8 +42,8 @@ class ResPartnerRevision(models.Model):
select=True, select=True,
required=True, required=True,
readonly=True) readonly=True)
change_ids = fields.One2many(comodel_name='res.partner.revision.change',
inverse_name='revision_id',
change_ids = fields.One2many(comodel_name='res.partner.changeset.change',
inverse_name='changeset_id',
string='Changes', string='Changes',
readonly=True) readonly=True)
date = fields.Datetime(default=fields.Datetime.now, date = fields.Datetime(default=fields.Datetime.now,
@ -76,14 +76,14 @@ class ResPartnerRevision(models.Model):
self.mapped('change_ids').cancel() self.mapped('change_ids').cancel()
@api.multi @api.multi
def add_revision(self, record, values):
""" Add a revision on a partner
def add_changeset(self, record, values):
""" Add a changeset on a partner
By default, when a partner is modified by a user or by the By default, when a partner is modified by a user or by the
system, the changes are applied and a validated revision is
system, the changes are applied and a validated changeset is
created. Callers which want to delegate the write of some created. Callers which want to delegate the write of some
fields to the revision must explicitly ask for it by providing a
key ``__revision_rules`` in the environment's context.
fields to the changeset must explicitly ask for it by providing a
key ``__changeset_rules`` in the environment's context.
Should be called before the execution of ``write`` on the record Should be called before the execution of ``write`` on the record
so we can keep track of the existing value and also because the so we can keep track of the existing value and also because the
@ -98,10 +98,10 @@ class ResPartnerRevision(models.Model):
""" """
record.ensure_one() record.ensure_one()
change_model = self.env['res.partner.revision.change']
change_model = self.env['res.partner.changeset.change']
write_values = values.copy() write_values = values.copy()
changes = [] changes = []
rules = self.env['revision.field.rule'].get_rules(record._model._name)
rules = self.env['changeset.field.rule'].get_rules(record._model._name)
for field in values: for field in values:
rule = rules.get(field) rule = rules.get(field)
if not rule: if not rule:
@ -110,14 +110,14 @@ class ResPartnerRevision(models.Model):
if not change_model._has_field_changed(record, field, if not change_model._has_field_changed(record, field,
values[field]): values[field]):
continue continue
change, pop_value = change_model._prepare_revision_change(
change, pop_value = change_model._prepare_changeset_change(
record, rule, field, values[field] record, rule, field, values[field]
) )
if pop_value: if pop_value:
write_values.pop(field) write_values.pop(field)
changes.append(change) changes.append(change)
if changes: if changes:
self.env['res.partner.revision'].create({
self.env['res.partner.changeset'].create({
'partner_id': record.id, 'partner_id': record.id,
'change_ids': [(0, 0, vals) for vals in changes], 'change_ids': [(0, 0, vals) for vals in changes],
'date': fields.Datetime.now(), 'date': fields.Datetime.now(),
@ -125,8 +125,8 @@ class ResPartnerRevision(models.Model):
return write_values return write_values
class ResPartnerRevisionChange(models.Model):
""" Store the change of one field for one revision on one partner
class ResPartnerChangesetChange(models.Model):
""" Store the change of one field for one changeset on one partner
This model is composed of 3 sets of fields: This model is composed of 3 sets of fields:
@ -142,7 +142,7 @@ class ResPartnerRevisionChange(models.Model):
the partner until the change is either applied either canceled, past the partner until the change is either applied either canceled, past
that it shows the 'old' value. that it shows the 'old' value.
The reason behind this is that the values may change on a partner between The reason behind this is that the values may change on a partner between
the moment when the revision is created and when it is applied.
the moment when the changeset is created and when it is applied.
On the views, we show the origin fields which represent the actual On the views, we show the origin fields which represent the actual
partner values or the old values and we show the new fields. partner values or the old values and we show the new fields.
@ -152,13 +152,13 @@ class ResPartnerRevisionChange(models.Model):
displayed on the form view so we benefit from their widgets. displayed on the form view so we benefit from their widgets.
""" """
_name = 'res.partner.revision.change'
_description = 'Partner Revision Change'
_name = 'res.partner.changeset.change'
_description = 'Partner Changeset Change'
_rec_name = 'field_id' _rec_name = 'field_id'
revision_id = fields.Many2one(comodel_name='res.partner.revision',
changeset_id = fields.Many2one(comodel_name='res.partner.changeset',
required=True, required=True,
string='Revision',
string='Changeset',
ondelete='cascade', ondelete='cascade',
readonly=True) readonly=True)
field_id = fields.Many2one(comodel_name='ir.model.fields', field_id = fields.Many2one(comodel_name='ir.model.fields',
@ -209,7 +209,7 @@ class ResPartnerRevisionChange(models.Model):
) )
# Fields storing the previous partner's values (saved when the # Fields storing the previous partner's values (saved when the
# revision is applied)
# changeset is applied)
old_value_char = fields.Char(string='Old', old_value_char = fields.Char(string='Old',
readonly=True) readonly=True)
old_value_date = fields.Date(string='Old', old_value_date = fields.Date(string='Old',
@ -288,11 +288,11 @@ class ResPartnerRevisionChange(models.Model):
_new_value_fields) _new_value_fields)
@api.one @api.one
@api.depends('revision_id.partner_id.*')
@api.depends('changeset_id.partner_id.*')
def _compute_origin_values(self): def _compute_origin_values(self):
field_name = self.get_field_for_type(self.field_id, 'origin') field_name = self.get_field_for_type(self.field_id, 'origin')
if self.state == 'draft': if self.state == 'draft':
value = self.revision_id.partner_id[self.field_id.name]
value = self.changeset_id.partner_id[self.field_id.name]
else: else:
old_field = self.get_field_for_type(self.field_id, 'old') old_field = self.get_field_for_type(self.field_id, 'old')
value = self[old_field] value = self[old_field]
@ -334,8 +334,8 @@ class ResPartnerRevisionChange(models.Model):
""" Copy the value of the partner to the 'old' field """ """ Copy the value of the partner to the 'old' field """
for change in self: for change in self:
# copy the existing partner's value for the history # copy the existing partner's value for the history
old_value_for_write = self._value_for_revision(
change.revision_id.partner_id,
old_value_for_write = self._value_for_changeset(
change.changeset_id.partner_id,
change.field_id.name change.field_id.name
) )
old_field_name = self.get_field_for_type(change.field_id, 'old') old_field_name = self.get_field_for_type(change.field_id, 'old')
@ -343,16 +343,16 @@ class ResPartnerRevisionChange(models.Model):
@api.multi @api.multi
def apply(self): def apply(self):
""" Apply the change on the revision's partner
""" Apply the change on the changeset's partner
It is optimized thus that it makes only one write on the partner It is optimized thus that it makes only one write on the partner
per revision if many changes are applied at once.
per changeset if many changes are applied at once.
""" """
changes_ok = self.browse() changes_ok = self.browse()
key = attrgetter('revision_id')
for revision, changes in groupby(self.sorted(key=key), key=key):
key = attrgetter('changeset_id')
for changeset, changes in groupby(self.sorted(key=key), key=key):
values = {} values = {}
partner = revision.partner_id
partner = changeset.partner_id
for change in changes: for change in changes:
if change.state in ('cancel', 'done'): if change.state in ('cancel', 'done'):
continue continue
@ -370,22 +370,22 @@ class ResPartnerRevisionChange(models.Model):
if not values: if not values:
continue continue
previous_revisions = self.env['res.partner.revision'].search(
[('date', '<', revision.date),
previous_changesets = self.env['res.partner.changeset'].search(
[('date', '<', changeset.date),
('state', '=', 'draft'), ('state', '=', 'draft'),
('partner_id', '=', revision.partner_id.id),
('partner_id', '=', changeset.partner_id.id),
], ],
limit=1, limit=1,
) )
if previous_revisions:
if previous_changesets:
raise exceptions.Warning( raise exceptions.Warning(
_('This change cannot be applied because a previous ' _('This change cannot be applied because a previous '
'revision for the same partner is pending.\n'
'Apply all the anterior revisions before applying '
'changeset for the same partner is pending.\n'
'Apply all the anterior changesets before applying '
'this one.') 'this one.')
) )
partner.with_context(__no_revision=True).write(values)
partner.with_context(__no_changeset=True).write(values)
changes_ok.write({'state': 'done'}) changes_ok.write({'state': 'done'})
@ -411,8 +411,8 @@ class ResPartnerRevisionChange(models.Model):
return model_field_def.convert_to_write(value) return model_field_def.convert_to_write(value)
@api.model @api.model
def _value_for_revision(self, record, field_name, value=_NO_VALUE):
""" Return a value from the record ready to write in a revision field
def _value_for_changeset(self, record, field_name, value=_NO_VALUE):
""" Return a value from the record ready to write in a changeset field
:param record: modified record :param record: modified record
:param field_name: name of the modified field :param field_name: name of the modified field
@ -431,23 +431,23 @@ class ResPartnerRevisionChange(models.Model):
return value return value
@api.multi @api.multi
def _prepare_revision_change(self, record, rule, field_name, value):
""" Prepare data for a revision change
def _prepare_changeset_change(self, record, rule, field_name, value):
""" Prepare data for a changeset change
It returns a dict of the values to write on the revision change
It returns a dict of the values to write on the changeset change
and a boolean that indicates if the value should be popped out and a boolean that indicates if the value should be popped out
of the values to write on the model. of the values to write on the model.
:returns: dict of values, boolean :returns: dict of values, boolean
""" """
new_field_name = self.get_field_for_type(rule.field_id, 'new') new_field_name = self.get_field_for_type(rule.field_id, 'new')
new_value = self._value_for_revision(record, field_name, value=value)
new_value = self._value_for_changeset(record, field_name, value=value)
change = { change = {
new_field_name: new_value, new_field_name: new_value,
'field_id': rule.field_id.id, 'field_id': rule.field_id.id,
} }
pop_value = False pop_value = False
if (not self.env.context.get('__revision_rules') or
if (not self.env.context.get('__changeset_rules') or
rule.action == 'auto'): rule.action == 'auto'):
change['state'] = 'done' change['state'] = 'done'
elif rule.action == 'validate': elif rule.action == 'validate':
@ -462,16 +462,16 @@ class ResPartnerRevisionChange(models.Model):
# button, but since we short circuit the 'apply', we # button, but since we short circuit the 'apply', we
# directly set the 'old' value here # directly set the 'old' value here
old_field_name = self.get_field_for_type(rule.field_id, 'old') old_field_name = self.get_field_for_type(rule.field_id, 'old')
# get values ready to write as expected by the revision
# get values ready to write as expected by the changeset
# (for instance, a many2one is written in a reference # (for instance, a many2one is written in a reference
# field) # field)
origin_value = self._value_for_revision(record, field_name)
origin_value = self._value_for_changeset(record, field_name)
change[old_field_name] = origin_value change[old_field_name] = origin_value
return change, pop_value return change, pop_value
def fields_view_get(self, *args, **kwargs): def fields_view_get(self, *args, **kwargs):
_super = super(ResPartnerRevisionChange, self)
_super = super(ResPartnerChangesetChange, self)
result = _super.fields_view_get(*args, **kwargs) result = _super.fields_view_get(*args, **kwargs)
if result['type'] != 'form': if result['type'] != 'form':
return return

12
partner_revision/models/revision_field_rule.py

@ -23,9 +23,9 @@ from openerp import models, fields, api
from openerp.tools.cache import ormcache from openerp.tools.cache import ormcache
class RevisionFieldRule(models.Model):
_name = 'revision.field.rule'
_description = 'Revision Field Rules'
class ChangesetFieldRule(models.Model):
_name = 'changeset.field.rule'
_description = 'Changeset Field Rules'
_rec_name = 'field_id' _rec_name = 'field_id'
model_id = fields.Many2one(comodel_name='ir.model', model_id = fields.Many2one(comodel_name='ir.model',
@ -62,18 +62,18 @@ class RevisionFieldRule(models.Model):
@api.model @api.model
def create(self, vals): def create(self, vals):
record = super(RevisionFieldRule, self).create(vals)
record = super(ChangesetFieldRule, self).create(vals)
self.clear_caches() self.clear_caches()
return record return record
@api.multi @api.multi
def write(self, vals): def write(self, vals):
result = super(RevisionFieldRule, self).write(vals)
result = super(ChangesetFieldRule, self).write(vals)
self.clear_caches() self.clear_caches()
return result return result
@api.multi @api.multi
def unlink(self): def unlink(self):
result = super(RevisionFieldRule, self).unlink()
result = super(ChangesetFieldRule, self).unlink()
self.clear_caches() self.clear_caches()
return result return result

18
partner_revision/security/ir.model.access.csv

@ -1,10 +1,10 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_view_revision_field_rule_partner_manager,revision field rules for partner managers,model_revision_field_rule,base.group_partner_manager,1,0,0,0
access_view_revision_field_rule_user,revision field rules for revision users,model_revision_field_rule,group_revision_user,1,0,0,0
access_view_revision_field_rule_manager,revision field rules for revision managers,model_revision_field_rule,group_revision_user,1,1,1,1
access_view_res_partner_revision_partner_manager,revision for partner managers,model_res_partner_revision,base.group_partner_manager,1,0,1,0
access_view_res_partner_revision_change_partner_manager,revision change for partner managers,model_res_partner_revision_change,base.group_partner_manager,1,0,1,0
access_view_res_partner_revision_user,revision for revision users,model_res_partner_revision,group_revision_user,1,1,1,0
access_view_res_partner_revision_change_user,revision change for revision users,model_res_partner_revision_change,group_revision_user,1,1,1,0
access_view_res_partner_revision_manager,revision for revision managers,model_res_partner_revision,group_revision_manager,1,1,1,1
access_view_res_partner_revision_change_manager,revision change for revision managers,model_res_partner_revision_change,group_revision_manager,1,1,1,1
access_view_changeset_field_rule_partner_manager,changeset field rules for partner managers,model_changeset_field_rule,base.group_partner_manager,1,0,0,0
access_view_changeset_field_rule_user,changeset field rules for changeset users,model_changeset_field_rule,group_changeset_user,1,0,0,0
access_view_changeset_field_rule_manager,changeset field rules for changeset managers,model_changeset_field_rule,group_changeset_user,1,1,1,1
access_view_res_partner_changeset_partner_manager,changeset for partner managers,model_res_partner_changeset,base.group_partner_manager,1,0,1,0
access_view_res_partner_changeset_change_partner_manager,changeset change for partner managers,model_res_partner_changeset_change,base.group_partner_manager,1,0,1,0
access_view_res_partner_changeset_user,changeset for changeset users,model_res_partner_changeset,group_changeset_user,1,1,1,0
access_view_res_partner_changeset_change_user,changeset change for changeset users,model_res_partner_changeset_change,group_changeset_user,1,1,1,0
access_view_res_partner_changeset_manager,changeset for changeset managers,model_res_partner_changeset,group_changeset_manager,1,1,1,1
access_view_res_partner_changeset_change_manager,changeset change for changeset managers,model_res_partner_changeset_change,group_changeset_manager,1,1,1,1

14
partner_revision/security/security.xml

@ -2,16 +2,16 @@
<openerp> <openerp>
<data> <data>
<record id="group_revision_manager" model="res.groups">
<field name="name">Revision Configuration</field>
<field name="comment">The user will have an access to the configuration of the revision rules.</field>
<record id="group_changeset_manager" model="res.groups">
<field name="name">Changeset Configuration</field>
<field name="comment">The user will have an access to the configuration of the changeset rules.</field>
<field name="users" eval="[(4, ref('base.user_root'))]"/> <field name="users" eval="[(4, ref('base.user_root'))]"/>
</record> </record>
<record id="group_revision_user" model="res.groups">
<field name="name">Revisions Validations </field>
<field name="comment">The user will be able to apply or reject revisions.</field>
<field name="implied_ids" eval="[(4, ref('group_revision_manager'))]"/>
<record id="group_changeset_user" model="res.groups">
<field name="name">Changesets Validations </field>
<field name="comment">The user will be able to apply or reject changesets.</field>
<field name="implied_ids" eval="[(4, ref('group_changeset_manager'))]"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/> <field name="users" eval="[(4, ref('base.user_root'))]"/>
</record> </record>

32
partner_revision/tests/common.py

@ -20,27 +20,27 @@
# #
class RevisionMixin(object):
class ChangesetMixin(object):
def assert_revision(self, partner, expected_changes):
""" Check if a revision has been created according to expected values
def assert_changeset(self, partner, expected_changes):
""" Check if a changeset has been created according to expected values
The partner should have no prior revision than the one created in the
test (so it has exactly 1 revision).
The partner should have no prior changeset than the one created in the
test (so it has exactly 1 changeset).
The expected changes are tuples with (field, origin_value, The expected changes are tuples with (field, origin_value,
new_value, state) new_value, state)
:param partner: record of partner having a revision
:param partner: record of partner having a changeset
:param expected_changes: contains tuples with the changes :param expected_changes: contains tuples with the changes
:type expected_changes: list(tuple)) :type expected_changes: list(tuple))
""" """
revision = self.env['res.partner.revision'].search(
changeset = self.env['res.partner.changeset'].search(
[('partner_id', '=', partner.id)], [('partner_id', '=', partner.id)],
) )
self.assertEqual(len(revision), 1,
"1 revision expected, got %s" % (revision,))
changes = revision.change_ids
self.assertEqual(len(changeset), 1,
"1 changeset expected, got %s" % (changeset,))
changes = changeset.change_ids
missing = [] missing = []
for expected_change in expected_changes: for expected_change in expected_changes:
for change in changes: for change in changes:
@ -67,15 +67,15 @@ class RevisionMixin(object):
if message: if message:
raise AssertionError('Changes do not match\n\n:%s' % message) raise AssertionError('Changes do not match\n\n:%s' % message)
def _create_revision(self, partner, changes):
""" Create a revision and its associated changes
def _create_changeset(self, partner, changes):
""" Create a changeset and its associated changes
:param partner: 'res.partner' record :param partner: 'res.partner' record
:param changes: list of changes [(field, new value, state)] :param changes: list of changes [(field, new value, state)]
:returns: 'res.partner.revision' record
:returns: 'res.partner.changeset' record
""" """
RevisionChange = self.env['res.partner.revision.change']
get_field = RevisionChange.get_field_for_type
ChangesetChange = self.env['res.partner.changeset.change']
get_field = ChangesetChange.get_field_for_type
change_values = [] change_values = []
for field, value, state in changes: for field, value, state in changes:
change = { change = {
@ -90,4 +90,4 @@ class RevisionMixin(object):
'partner_id': partner.id, 'partner_id': partner.id,
'change_ids': change_values, 'change_ids': change_values,
} }
return self.env['res.partner.revision'].create(values)
return self.env['res.partner.changeset'].create(values)

146
partner_revision/tests/test_revision_field_type.py

@ -20,14 +20,14 @@
# #
from openerp.tests import common from openerp.tests import common
from .common import RevisionMixin
from .common import ChangesetMixin
class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
""" Check that revision changes are stored expectingly to their types """
class TestChangesetFieldType(ChangesetMixin, common.TransactionCase):
""" Check that changeset changes are stored expectingly to their types """
def _setup_rules(self): def _setup_rules(self):
RevisionFieldRule = self.env['revision.field.rule']
ChangesetFieldRule = self.env['changeset.field.rule']
partner_model_id = self.env.ref('base.model_res_partner').id partner_model_id = self.env.ref('base.model_res_partner').id
fields = (('char', 'ref'), fields = (('char', 'ref'),
('text', 'comment'), ('text', 'comment'),
@ -50,217 +50,217 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
# set attribute such as 'self.field_char' is a # set attribute such as 'self.field_char' is a
# ir.model.fields record of the field res_partner.ref # ir.model.fields record of the field res_partner.ref
setattr(self, attr_name, field_record) setattr(self, attr_name, field_record)
RevisionFieldRule.create({
ChangesetFieldRule.create({
'model_id': partner_model_id, 'model_id': partner_model_id,
'field_id': field_record.id, 'field_id': field_record.id,
'action': 'validate', 'action': 'validate',
}) })
def setUp(self): def setUp(self):
super(TestRevisionFieldType, self).setUp()
super(TestChangesetFieldType, self).setUp()
self._setup_rules() self._setup_rules()
self.partner = self.env['res.partner'].create({ self.partner = self.env['res.partner'].create({
'name': 'Original Name', 'name': 'Original Name',
'street': 'Original Street', 'street': 'Original Street',
}) })
def test_new_revision_char(self):
""" Add a new revision on a Char field """
self.partner.with_context(__revision_rules=True).write({
def test_new_changeset_char(self):
""" Add a new changeset on a Char field """
self.partner.with_context(__changeset_rules=True).write({
self.field_char.name: 'New value', self.field_char.name: 'New value',
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_char, self.partner[self.field_char.name], [(self.field_char, self.partner[self.field_char.name],
'New value', 'draft'), '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({
def test_new_changeset_text(self):
""" Add a new changeset on a Text field """
self.partner.with_context(__changeset_rules=True).write({
self.field_text.name: 'New comment\non 2 lines', self.field_text.name: 'New comment\non 2 lines',
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_text, self.partner[self.field_text.name], [(self.field_text, self.partner[self.field_text.name],
'New comment\non 2 lines', 'draft'), '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({
def test_new_changeset_boolean(self):
""" Add a new changeset on a Boolean field """
# ensure the changeset has to change the value
self.partner.with_context(__no_changeset=True).write({
self.field_boolean.name: False, self.field_boolean.name: False,
}) })
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
self.field_boolean.name: True, self.field_boolean.name: True,
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_boolean, self.partner[self.field_boolean.name], [(self.field_boolean, self.partner[self.field_boolean.name],
True, 'draft'), True, 'draft'),
] ]
) )
def test_new_revision_date(self):
""" Add a new revision on a Date field """
self.partner.with_context(__revision_rules=True).write({
def test_new_changeset_date(self):
""" Add a new changeset on a Date field """
self.partner.with_context(__changeset_rules=True).write({
self.field_date.name: '2015-09-15', self.field_date.name: '2015-09-15',
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_date, self.partner[self.field_date.name], [(self.field_date, self.partner[self.field_date.name],
'2015-09-15', 'draft'), '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({
def test_new_changeset_integer(self):
""" Add a new changeset on a Integer field """
self.partner.with_context(__changeset_rules=True).write({
self.field_integer.name: 42, self.field_integer.name: 42,
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_integer, self.partner[self.field_integer.name], [(self.field_integer, self.partner[self.field_integer.name],
42, 'draft'), 42, 'draft'),
] ]
) )
def test_new_revision_float(self):
""" Add a new revision on a Float field """
self.partner.with_context(__revision_rules=True).write({
def test_new_changeset_float(self):
""" Add a new changeset on a Float field """
self.partner.with_context(__changeset_rules=True).write({
self.field_float.name: 3.1415, self.field_float.name: 3.1415,
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_float, self.partner[self.field_float.name], [(self.field_float, self.partner[self.field_float.name],
3.1415, 'draft'), 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({
def test_new_changeset_selection(self):
""" Add a new changeset on a Selection field """
self.partner.with_context(__changeset_rules=True).write({
self.field_selection.name: 'delivery', self.field_selection.name: 'delivery',
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_selection, self.partner[self.field_selection.name], [(self.field_selection, self.partner[self.field_selection.name],
'delivery', 'draft'), 'delivery', 'draft'),
] ]
) )
def test_new_revision_many2one(self):
""" Add a new revision on a Many2one field """
self.partner.with_context(__no_revision=True).write({
def test_new_changeset_many2one(self):
""" Add a new changeset on a Many2one field """
self.partner.with_context(__no_changeset=True).write({
self.field_many2one.name: self.env.ref('base.fr').id, self.field_many2one.name: self.env.ref('base.fr').id,
}) })
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
self.field_many2one.name: self.env.ref('base.ch').id, self.field_many2one.name: self.env.ref('base.ch').id,
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_many2one, self.partner[self.field_many2one.name], [(self.field_many2one, self.partner[self.field_many2one.name],
self.env.ref('base.ch'), 'draft'), self.env.ref('base.ch'), 'draft'),
] ]
) )
def test_new_revision_many2many(self):
""" Add a new revision on a Many2many field is not supported """
def test_new_changeset_many2many(self):
""" Add a new changeset on a Many2many field is not supported """
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
self.field_many2many.name: [self.env.ref('base.ch').id], 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 """
def test_new_changeset_one2many(self):
""" Add a new changeset on a One2many field is not supported """
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
self.field_one2many.name: [self.env.ref('base.user_root').id], 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 """
def test_new_changeset_binary(self):
""" Add a new changeset on a Binary field is not supported """
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
self.field_binary.name: '', self.field_binary.name: '',
}) })
def test_apply_char(self): def test_apply_char(self):
""" Apply a change on a Char field """ """ Apply a change on a Char field """
changes = [(self.field_char, 'New Ref', 'draft')] changes = [(self.field_char, 'New Ref', 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner[self.field_char.name], 'New Ref') self.assertEqual(self.partner[self.field_char.name], 'New Ref')
def test_apply_text(self): def test_apply_text(self):
""" Apply a change on a Text field """ """ Apply a change on a Text field """
changes = [(self.field_text, 'New comment\non 2 lines', 'draft')] changes = [(self.field_text, 'New comment\non 2 lines', 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner[self.field_text.name], self.assertEqual(self.partner[self.field_text.name],
'New comment\non 2 lines') 'New comment\non 2 lines')
def test_apply_boolean(self): def test_apply_boolean(self):
""" Apply a change on a Boolean field """ """ Apply a change on a Boolean field """
# ensure the revision has to change the value
# ensure the changeset has to change the value
self.partner.write({self.field_boolean.name: False}) self.partner.write({self.field_boolean.name: False})
changes = [(self.field_boolean, True, 'draft')] changes = [(self.field_boolean, True, 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner[self.field_boolean.name], True) self.assertEqual(self.partner[self.field_boolean.name], True)
changes = [(self.field_boolean, False, 'draft')] changes = [(self.field_boolean, False, 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner[self.field_boolean.name], False) self.assertEqual(self.partner[self.field_boolean.name], False)
def test_apply_date(self): def test_apply_date(self):
""" Apply a change on a Date field """ """ Apply a change on a Date field """
changes = [(self.field_date, '2015-09-15', 'draft')] changes = [(self.field_date, '2015-09-15', 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertAlmostEqual(self.partner[self.field_date.name], self.assertAlmostEqual(self.partner[self.field_date.name],
'2015-09-15') '2015-09-15')
def test_apply_integer(self): def test_apply_integer(self):
""" Apply a change on a Integer field """ """ Apply a change on a Integer field """
changes = [(self.field_integer, 42, 'draft')] changes = [(self.field_integer, 42, 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertAlmostEqual(self.partner[self.field_integer.name], 42) self.assertAlmostEqual(self.partner[self.field_integer.name], 42)
def test_apply_float(self): def test_apply_float(self):
""" Apply a change on a Float field """ """ Apply a change on a Float field """
changes = [(self.field_float, 52.47, 'draft')] changes = [(self.field_float, 52.47, 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertAlmostEqual(self.partner[self.field_float.name], 52.47) self.assertAlmostEqual(self.partner[self.field_float.name], 52.47)
def test_apply_selection(self): def test_apply_selection(self):
""" Apply a change on a Selection field """ """ Apply a change on a Selection field """
changes = [(self.field_selection, 'delivery', 'draft')] changes = [(self.field_selection, 'delivery', 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertAlmostEqual(self.partner[self.field_selection.name], self.assertAlmostEqual(self.partner[self.field_selection.name],
'delivery') 'delivery')
def test_apply_many2one(self): def test_apply_many2one(self):
""" Apply a change on a Many2one field """ """ Apply a change on a Many2one field """
self.partner.with_context(__no_revision=True).write({
self.partner.with_context(__no_changeset=True).write({
self.field_many2one.name: self.env.ref('base.fr').id, self.field_many2one.name: self.env.ref('base.fr').id,
}) })
changes = [(self.field_many2one, changes = [(self.field_many2one,
'res.country,%d' % self.env.ref('base.ch').id, 'res.country,%d' % self.env.ref('base.ch').id,
'draft')] 'draft')]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner[self.field_many2one.name], self.assertEqual(self.partner[self.field_many2one.name],
self.env.ref('base.ch')) self.env.ref('base.ch'))
@ -270,7 +270,7 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
self.env.ref('base.ch').id, self.env.ref('base.ch').id,
'draft')] 'draft')]
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self._create_revision(self.partner, changes)
self._create_changeset(self.partner, changes)
def test_apply_one2many(self): def test_apply_one2many(self):
""" Apply a change on a One2many field is not supported """ """ Apply a change on a One2many field is not supported """
@ -280,10 +280,10 @@ class TestRevisionFieldType(RevisionMixin, common.TransactionCase):
], ],
'draft')] 'draft')]
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self._create_revision(self.partner, changes)
self._create_changeset(self.partner, changes)
def test_apply_binary(self): def test_apply_binary(self):
""" Apply a change on a Binary field is not supported """ """ Apply a change on a Binary field is not supported """
changes = [(self.field_one2many, '', 'draft')] changes = [(self.field_one2many, '', 'draft')]
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self._create_revision(self.partner, changes)
self._create_changeset(self.partner, changes)

150
partner_revision/tests/test_revision_flow.py

@ -23,50 +23,50 @@ from datetime import datetime, timedelta
from openerp import fields, exceptions from openerp import fields, exceptions
from openerp.tests import common from openerp.tests import common
from .common import RevisionMixin
from .common import ChangesetMixin
class TestRevisionFlow(RevisionMixin, common.TransactionCase):
""" Check how revision are generated and applied based on the rules.
class TestChangesetFlow(ChangesetMixin, common.TransactionCase):
""" Check how changeset are generated and applied based on the rules.
We do not really care about the types of the fields in this test We do not really care about the types of the fields in this test
suite, so we only use 'char' fields. We have to ensure that the suite, so we only use 'char' fields. We have to ensure that the
general revision flows work as expected, that is:
general changeset flows work as expected, that is:
* create a 'done' revision when a manual/system write is made on partner
* create a revision according to the revision rules when the key
'__revision_rules' is passed in the context
* apply a revision change writes the value on the partner
* apply a whole revision writes all the changes' values on the partner
* create a 'done' changeset when a manual/system write is made on partner
* create a changeset according to the changeset rules when the key
'__changeset_rules' is passed in the context
* apply a changeset change writes the value on the partner
* apply a whole changeset writes all the changes' values on the partner
* changes in state 'cancel' or 'done' do not write on the partner * changes in state 'cancel' or 'done' do not write on the partner
* when all the changes are either 'cancel' or 'done', the revision
* when all the changes are either 'cancel' or 'done', the changeset
becomes 'done' becomes 'done'
""" """
def _setup_rules(self): def _setup_rules(self):
RevisionFieldRule = self.env['revision.field.rule']
ChangesetFieldRule = self.env['changeset.field.rule']
partner_model_id = self.env.ref('base.model_res_partner').id partner_model_id = self.env.ref('base.model_res_partner').id
self.field_name = self.env.ref('base.field_res_partner_name') self.field_name = self.env.ref('base.field_res_partner_name')
self.field_street = self.env.ref('base.field_res_partner_street') self.field_street = self.env.ref('base.field_res_partner_street')
self.field_street2 = self.env.ref('base.field_res_partner_street2') self.field_street2 = self.env.ref('base.field_res_partner_street2')
RevisionFieldRule.create({
ChangesetFieldRule.create({
'model_id': partner_model_id, 'model_id': partner_model_id,
'field_id': self.field_name.id, 'field_id': self.field_name.id,
'action': 'auto', 'action': 'auto',
}) })
RevisionFieldRule.create({
ChangesetFieldRule.create({
'model_id': partner_model_id, 'model_id': partner_model_id,
'field_id': self.field_street.id, 'field_id': self.field_street.id,
'action': 'validate', 'action': 'validate',
}) })
RevisionFieldRule.create({
ChangesetFieldRule.create({
'model_id': partner_model_id, 'model_id': partner_model_id,
'field_id': self.field_street2.id, 'field_id': self.field_street2.id,
'action': 'never', 'action': 'never',
}) })
def setUp(self): def setUp(self):
super(TestRevisionFlow, self).setUp()
super(TestChangesetFlow, self).setUp()
self._setup_rules() self._setup_rules()
self.partner = self.env['res.partner'].create({ self.partner = self.env['res.partner'].create({
'name': 'X', 'name': 'X',
@ -74,18 +74,18 @@ class TestRevisionFlow(RevisionMixin, common.TransactionCase):
'street2': 'street2 X', 'street2': 'street2 X',
}) })
def test_new_revision(self):
""" Add a new revision on a partner
def test_new_changeset(self):
""" Add a new changeset on a partner
A new revision is created when we write on a partner with
``__revision_rules`` in the context.
A new changeset is created when we write on a partner with
``__changeset_rules`` in the context.
""" """
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
'name': 'Y', 'name': 'Y',
'street': 'street Y', 'street': 'street Y',
'street2': 'street2 Y', 'street2': 'street2 Y',
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_name, 'X', 'Y', 'done'), [(self.field_name, 'X', 'Y', 'done'),
(self.field_street, 'street X', 'street Y', 'draft'), (self.field_street, 'street X', 'street Y', 'draft'),
@ -96,12 +96,12 @@ class TestRevisionFlow(RevisionMixin, common.TransactionCase):
self.assertEqual(self.partner.street, 'street X') self.assertEqual(self.partner.street, 'street X')
self.assertEqual(self.partner.street2, 'street2 X') self.assertEqual(self.partner.street2, 'street2 X')
def test_new_revision_empty_value(self):
""" Create a revision change that empty a value """
self.partner.with_context(__revision_rules=True).write({
def test_new_changeset_empty_value(self):
""" Create a changeset change that empty a value """
self.partner.with_context(__changeset_rules=True).write({
'street': False, 'street': False,
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_street, 'street X', False, 'draft')] [(self.field_street, 'street X', False, 'draft')]
) )
@ -109,14 +109,14 @@ class TestRevisionFlow(RevisionMixin, common.TransactionCase):
def test_manual_edition(self): def test_manual_edition(self):
""" A manual edition of a partner should always be applied """ A manual edition of a partner should always be applied
But should create a 'done' revision
But should create a 'done' changeset
""" """
self.partner.write({ self.partner.write({
'name': 'Y', 'name': 'Y',
'street': 'street Y', 'street': 'street Y',
'street2': 'street2 Y', 'street2': 'street2 Y',
}) })
self.assert_revision(
self.assert_changeset(
self.partner, self.partner,
[(self.field_name, 'X', 'Y', 'done'), [(self.field_name, 'X', 'Y', 'done'),
(self.field_street, 'street X', 'street Y', 'done'), (self.field_street, 'street X', 'street Y', 'done'),
@ -128,22 +128,22 @@ class TestRevisionFlow(RevisionMixin, common.TransactionCase):
self.assertEqual(self.partner.street2, 'street2 Y') self.assertEqual(self.partner.street2, 'street2 Y')
def test_apply_change(self): def test_apply_change(self):
""" Apply a revision change on a partner """
""" Apply a changeset change on a partner """
changes = [ changes = [
(self.field_name, 'Y', 'draft'), (self.field_name, 'Y', 'draft'),
] ]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner.name, 'Y') self.assertEqual(self.partner.name, 'Y')
self.assertEqual(revision.change_ids.state, 'done')
self.assertEqual(changeset.change_ids.state, 'done')
def test_apply_done_change(self): def test_apply_done_change(self):
""" Done changes do not apply (already applied) """ """ Done changes do not apply (already applied) """
changes = [ changes = [
(self.field_name, 'Y', 'done'), (self.field_name, 'Y', 'done'),
] ]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner.name, 'X') self.assertEqual(self.partner.name, 'X')
def test_apply_cancel_change(self): def test_apply_cancel_change(self):
@ -151,8 +151,8 @@ class TestRevisionFlow(RevisionMixin, common.TransactionCase):
changes = [ changes = [
(self.field_name, 'Y', 'cancel'), (self.field_name, 'Y', 'cancel'),
] ]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner.name, 'X') self.assertEqual(self.partner.name, 'X')
def test_apply_empty_value(self): def test_apply_empty_value(self):
@ -160,8 +160,8 @@ class TestRevisionFlow(RevisionMixin, common.TransactionCase):
changes = [ changes = [
(self.field_street, False, 'draft'), (self.field_street, False, 'draft'),
] ]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertFalse(self.partner.street) self.assertFalse(self.partner.street)
def test_apply_change_loop(self): def test_apply_change_loop(self):
@ -171,83 +171,83 @@ class TestRevisionFlow(RevisionMixin, common.TransactionCase):
(self.field_street, 'street Y', 'draft'), (self.field_street, 'street Y', 'draft'),
(self.field_street2, 'street2 Y', 'draft'), (self.field_street2, 'street2 Y', 'draft'),
] ]
revision = self._create_revision(self.partner, changes)
revision.change_ids.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.change_ids.apply()
self.assertEqual(self.partner.name, 'Y') self.assertEqual(self.partner.name, 'Y')
self.assertEqual(self.partner.street, 'street Y') self.assertEqual(self.partner.street, 'street Y')
self.assertEqual(self.partner.street2, 'street2 Y') self.assertEqual(self.partner.street2, 'street2 Y')
def test_apply(self): def test_apply(self):
""" Apply a full revision on a partner """
""" Apply a full changeset on a partner """
changes = [ changes = [
(self.field_name, 'Y', 'draft'), (self.field_name, 'Y', 'draft'),
(self.field_street, 'street Y', 'draft'), (self.field_street, 'street Y', 'draft'),
(self.field_street2, 'street2 Y', 'draft'), (self.field_street2, 'street2 Y', 'draft'),
] ]
revision = self._create_revision(self.partner, changes)
revision.apply()
changeset = self._create_changeset(self.partner, changes)
changeset.apply()
self.assertEqual(self.partner.name, 'Y') self.assertEqual(self.partner.name, 'Y')
self.assertEqual(self.partner.street, 'street Y') self.assertEqual(self.partner.street, 'street Y')
self.assertEqual(self.partner.street2, 'street2 Y') self.assertEqual(self.partner.street2, 'street2 Y')
def test_revision_state_on_done(self):
""" Check that revision state becomes done when changes are done """
def test_changeset_state_on_done(self):
""" Check that changeset state becomes done when changes are done """
changes = [(self.field_name, 'Y', 'draft')] changes = [(self.field_name, 'Y', 'draft')]
revision = self._create_revision(self.partner, changes)
self.assertEqual(revision.state, 'draft')
revision.change_ids.apply()
self.assertEqual(revision.state, 'done')
changeset = self._create_changeset(self.partner, changes)
self.assertEqual(changeset.state, 'draft')
changeset.change_ids.apply()
self.assertEqual(changeset.state, 'done')
def test_revision_state_on_cancel(self):
def test_changeset_state_on_cancel(self):
""" Check that rev. state becomes done when changes are canceled """ """ Check that rev. state becomes done when changes are canceled """
changes = [(self.field_name, 'Y', 'draft')] changes = [(self.field_name, 'Y', 'draft')]
revision = self._create_revision(self.partner, changes)
self.assertEqual(revision.state, 'draft')
revision.change_ids.cancel()
self.assertEqual(revision.state, 'done')
changeset = self._create_changeset(self.partner, changes)
self.assertEqual(changeset.state, 'draft')
changeset.change_ids.cancel()
self.assertEqual(changeset.state, 'done')
def test_revision_state(self):
""" Check that revision state becomes done with multiple changes """
def test_changeset_state(self):
""" Check that changeset state becomes done with multiple changes """
changes = [ changes = [
(self.field_name, 'Y', 'draft'), (self.field_name, 'Y', 'draft'),
(self.field_street, 'street Y', 'draft'), (self.field_street, 'street Y', 'draft'),
(self.field_street2, 'street2 Y', 'draft'), (self.field_street2, 'street2 Y', 'draft'),
] ]
revision = self._create_revision(self.partner, changes)
self.assertEqual(revision.state, 'draft')
revision.apply()
self.assertEqual(revision.state, 'done')
changeset = self._create_changeset(self.partner, changes)
self.assertEqual(changeset.state, 'draft')
changeset.apply()
self.assertEqual(changeset.state, 'done')
def test_apply_revision_with_other_pending(self):
""" Error when applying when previous pending revisions exist """
def test_apply_changeset_with_other_pending(self):
""" Error when applying when previous pending changesets exist """
changes = [(self.field_name, 'Y', 'draft')] changes = [(self.field_name, 'Y', 'draft')]
old_revision = self._create_revision(self.partner, changes)
# if the date is the same, both revision can be applied
old_changeset = self._create_changeset(self.partner, changes)
# if the date is the same, both changeset can be applied
to_string = fields.Datetime.to_string to_string = fields.Datetime.to_string
old_revision.date = to_string(datetime.now() - timedelta(days=1))
old_changeset.date = to_string(datetime.now() - timedelta(days=1))
changes = [(self.field_name, 'Z', 'draft')] changes = [(self.field_name, 'Z', 'draft')]
revision = self._create_revision(self.partner, changes)
changeset = self._create_changeset(self.partner, changes)
with self.assertRaises(exceptions.Warning): with self.assertRaises(exceptions.Warning):
revision.change_ids.apply()
changeset.change_ids.apply()
def test_apply_different_revisions(self):
""" Apply different revisions at once """
def test_apply_different_changesets(self):
""" Apply different changesets at once """
partner2 = self.env['res.partner'].create({'name': 'P2'}) partner2 = self.env['res.partner'].create({'name': 'P2'})
changes = [ changes = [
(self.field_name, 'Y', 'draft'), (self.field_name, 'Y', 'draft'),
(self.field_street, 'street Y', 'draft'), (self.field_street, 'street Y', 'draft'),
(self.field_street2, 'street2 Y', 'draft'), (self.field_street2, 'street2 Y', 'draft'),
] ]
revision = self._create_revision(self.partner, changes)
revision2 = self._create_revision(partner2, changes)
self.assertEqual(revision.state, 'draft')
self.assertEqual(revision2.state, 'draft')
(revision + revision2).apply()
changeset = self._create_changeset(self.partner, changes)
changeset2 = self._create_changeset(partner2, changes)
self.assertEqual(changeset.state, 'draft')
self.assertEqual(changeset2.state, 'draft')
(changeset + changeset2).apply()
self.assertEqual(self.partner.name, 'Y') self.assertEqual(self.partner.name, 'Y')
self.assertEqual(self.partner.street, 'street Y') self.assertEqual(self.partner.street, 'street Y')
self.assertEqual(self.partner.street2, 'street2 Y') self.assertEqual(self.partner.street2, 'street2 Y')
self.assertEqual(partner2.name, 'Y') self.assertEqual(partner2.name, 'Y')
self.assertEqual(partner2.street, 'street Y') self.assertEqual(partner2.street, 'street Y')
self.assertEqual(partner2.street2, 'street2 Y') self.assertEqual(partner2.street2, 'street2 Y')
self.assertEqual(revision.state, 'done')
self.assertEqual(revision2.state, 'done')
self.assertEqual(changeset.state, 'done')
self.assertEqual(changeset2.state, 'done')

34
partner_revision/tests/test_revision_origin.py

@ -20,10 +20,10 @@
# #
from openerp.tests import common from openerp.tests import common
from .common import RevisionMixin
from .common import ChangesetMixin
class TestRevisionOrigin(RevisionMixin, common.TransactionCase):
class TestChangesetOrigin(ChangesetMixin, common.TransactionCase):
""" Check that origin - old fields are stored as expected. """ Check that origin - old fields are stored as expected.
'origin' fields dynamically read fields from the partner when the state 'origin' fields dynamically read fields from the partner when the state
@ -33,17 +33,17 @@ class TestRevisionOrigin(RevisionMixin, common.TransactionCase):
""" """
def _setup_rules(self): def _setup_rules(self):
RevisionFieldRule = self.env['revision.field.rule']
ChangesetFieldRule = self.env['changeset.field.rule']
partner_model_id = self.env.ref('base.model_res_partner').id partner_model_id = self.env.ref('base.model_res_partner').id
self.field_name = self.env.ref('base.field_res_partner_name') self.field_name = self.env.ref('base.field_res_partner_name')
RevisionFieldRule.create({
ChangesetFieldRule.create({
'model_id': partner_model_id, 'model_id': partner_model_id,
'field_id': self.field_name.id, 'field_id': self.field_name.id,
'action': 'validate', 'action': 'validate',
}) })
def setUp(self): def setUp(self):
super(TestRevisionOrigin, self).setUp()
super(TestChangesetOrigin, self).setUp()
self._setup_rules() self._setup_rules()
self.partner = self.env['res.partner'].create({ self.partner = self.env['res.partner'].create({
'name': 'X', 'name': 'X',
@ -54,11 +54,11 @@ class TestRevisionOrigin(RevisionMixin, common.TransactionCase):
According to the state of the change. According to the state of the change.
""" """
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
'name': 'Y', 'name': 'Y',
}) })
revision = self.partner.revision_ids
change = revision.change_ids
changeset = self.partner.changeset_ids
change = changeset.change_ids
self.assertEqual(self.partner.name, 'X') self.assertEqual(self.partner.name, 'X')
self.assertEqual(change.origin_value_char, 'X') self.assertEqual(change.origin_value_char, 'X')
self.assertEqual(change.origin_value_display, 'X') self.assertEqual(change.origin_value_display, 'X')
@ -77,11 +77,11 @@ class TestRevisionOrigin(RevisionMixin, common.TransactionCase):
According to the state of the change. According to the state of the change.
""" """
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
'name': 'Y', 'name': 'Y',
}) })
revision = self.partner.revision_ids
change = revision.change_ids
changeset = self.partner.changeset_ids
change = changeset.change_ids
self.assertEqual(self.partner.name, 'X') self.assertEqual(self.partner.name, 'X')
self.assertEqual(change.origin_value_char, 'X') self.assertEqual(change.origin_value_char, 'X')
self.assertEqual(change.origin_value_display, 'X') self.assertEqual(change.origin_value_display, 'X')
@ -97,11 +97,11 @@ class TestRevisionOrigin(RevisionMixin, common.TransactionCase):
def test_old_field_of_change_with_apply(self): def test_old_field_of_change_with_apply(self):
""" Old field is stored when the change is applied """ """ Old field is stored when the change is applied """
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
'name': 'Y', 'name': 'Y',
}) })
revision = self.partner.revision_ids
change = revision.change_ids
changeset = self.partner.changeset_ids
change = changeset.change_ids
self.assertEqual(self.partner.name, 'X') self.assertEqual(self.partner.name, 'X')
self.assertFalse(change.old_value_char) self.assertFalse(change.old_value_char)
self.partner.write({'name': 'A'}) self.partner.write({'name': 'A'})
@ -113,11 +113,11 @@ class TestRevisionOrigin(RevisionMixin, common.TransactionCase):
def test_old_field_of_change_with_cancel(self): def test_old_field_of_change_with_cancel(self):
""" Old field is stored when the change is canceled """ """ Old field is stored when the change is canceled """
self.partner.with_context(__revision_rules=True).write({
self.partner.with_context(__changeset_rules=True).write({
'name': 'Y', 'name': 'Y',
}) })
revision = self.partner.revision_ids
change = revision.change_ids
changeset = self.partner.changeset_ids
change = changeset.change_ids
self.assertEqual(self.partner.name, 'X') self.assertEqual(self.partner.name, 'X')
self.assertFalse(change.old_value_char) self.assertFalse(change.old_value_char)
self.partner.write({'name': 'A'}) self.partner.write({'name': 'A'})

6
partner_revision/views/menu.xml

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data noupdate="0"> <data noupdate="0">
<menuitem id="menu_revision"
name="Partner Revisions"
groups="group_revision_user"
<menuitem id="menu_changeset"
name="Partner Changesets"
groups="group_changeset_user"
parent="base.menu_base_config" parent="base.menu_base_config"
sequence="20"/> sequence="20"/>
</data> </data>

51
partner_revision/views/res_partner_revision_views.xml

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data noupdate="0"> <data noupdate="0">
<record id="view_res_partner_revision_tree" model="ir.ui.view">
<field name="name">res.partner.revision.tree</field>
<field name="model">res.partner.revision</field>
<record id="view_res_partner_changeset_tree" model="ir.ui.view">
<field name="name">res.partner.changeset.tree</field>
<field name="model">res.partner.changeset</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Partner Revision" delete="false" create="false">
<tree string="Partner Changeset" delete="false" create="false">
<field name="partner_id"/> <field name="partner_id"/>
<field name="date"/> <field name="date"/>
<field name="state"/> <field name="state"/>
@ -13,11 +13,11 @@
</field> </field>
</record> </record>
<record id="view_res_partner_revision_form" model="ir.ui.view">
<field name="name">res.partner.revision.form</field>
<field name="model">res.partner.revision</field>
<record id="view_res_partner_changeset_form" model="ir.ui.view">
<field name="name">res.partner.changeset.form</field>
<field name="model">res.partner.changeset</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Partner Revision" delete="false" create="false">
<form string="Partner Changeset" delete="false" create="false">
<header> <header>
<button name="apply" <button name="apply"
string="Apply pending changes" type="object" string="Apply pending changes" type="object"
@ -30,14 +30,14 @@
<field name="state" widget="statusbar" <field name="state" widget="statusbar"
statusbar_visible="draft,done"/> statusbar_visible="draft,done"/>
</header> </header>
<sheet string="Partner Revision">
<sheet string="Partner Changeset">
<group> <group>
<field name="partner_id"/> <field name="partner_id"/>
<field name="date"/> <field name="date"/>
</group> </group>
<group string="Changes"> <group string="Changes">
<field name="change_ids" nolabel="1"> <field name="change_ids" nolabel="1">
<tree string="Partner Revision Change">
<tree string="Partner Changeset Change">
<field name="field_id" context="{'no_open': true}"/> <field name="field_id" context="{'no_open': true}"/>
<field name="field_type" invisible="1"/> <field name="field_type" invisible="1"/>
@ -64,11 +64,11 @@
</field> </field>
</record> </record>
<record id="view_res_partner_revision_change_form" model="ir.ui.view">
<field name="name">res.partner.revision.change.form</field>
<field name="model">res.partner.revision.change</field>
<record id="view_res_partner_changeset_change_form" model="ir.ui.view">
<field name="name">res.partner.changeset.change.form</field>
<field name="model">res.partner.changeset.change</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Partner Revision Change" delete="false" create="false">
<form string="Partner Changeset Change" delete="false" create="false">
<header> <header>
<button name="apply" <button name="apply"
string="Apply" type="object" string="Apply" type="object"
@ -117,11 +117,11 @@
</field> </field>
</record> </record>
<record id="view_res_partner_revision_search" model="ir.ui.view">
<field name="name">res.partner.revision.search</field>
<field name="model">res.partner.revision</field>
<record id="view_res_partner_changeset_search" model="ir.ui.view">
<field name="name">res.partner.changeset.search</field>
<field name="model">res.partner.changeset</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Partner Revision">
<search string="Partner Changeset">
<field name="partner_id"/> <field name="partner_id"/>
<filter string="Pending" name="draft" <filter string="Pending" name="draft"
domain="[('state','=','draft')]"/> domain="[('state','=','draft')]"/>
@ -139,19 +139,20 @@
</field> </field>
</record> </record>
<record model="ir.actions.act_window" id="action_res_partner_revision_view">
<field name="name">Partner Revision</field>
<record model="ir.actions.act_window" id="action_res_partner_changeset_view">
<field name="name">Partner Changeset</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner.revision</field>
<field name="res_model">res.partner.changeset</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="context">{'search_default_draft': 1}</field> <field name="context">{'search_default_draft': 1}</field>
<field name="search_view_id" ref="view_res_partner_revision_search"/>
<field name="search_view_id" ref="view_res_partner_changeset_search"/>
</record> </record>
<menuitem id="menu_res_partner_revision"
parent="menu_revision"
<menuitem id="menu_res_partner_changeset"
parent="menu_changeset"
sequence="20" sequence="20"
action="action_res_partner_revision_view"/>
name="Changesets"
action="action_res_partner_changeset_view"/>
</data> </data>
</openerp> </openerp>

16
partner_revision/views/res_partner_views.xml

@ -7,17 +7,17 @@
<field name="model">res.partner</field> <field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" /> <field name="inherit_id" ref="base.view_partner_form" />
<field name="priority" eval="18"/> <field name="priority" eval="18"/>
<field name="groups_id" eval="[(4, ref('partner_revision.group_revision_user'))]"/>
<field name="groups_id" eval="[(4, ref('partner_revision.group_changeset_user'))]"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside"> <xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" <button class="oe_inline oe_stat_button"
type="action" type="action"
name="%(partner_revision.action_res_partner_revision_view)d"
attrs="{'invisible': [('count_pending_revisions', '=', 0)]}"
name="%(partner_revision.action_res_partner_changeset_view)d"
attrs="{'invisible': [('count_pending_changesets', '=', 0)]}"
context="{'search_default_draft': 1, 'search_default_partner_id': active_id}" context="{'search_default_draft': 1, 'search_default_partner_id': active_id}"
icon="fa-code-fork"> icon="fa-code-fork">
<field string="Pending Revisions"
name="count_pending_revisions"
<field string="Pending Changesets"
name="count_pending_changesets"
widget="statinfo"/> widget="statinfo"/>
</button> </button>
</xpath> </xpath>
@ -31,9 +31,9 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<filter name="customer" position="after"> <filter name="customer" position="after">
<separator/> <separator/>
<filter string="Pending Revisions"
name="pending_revisions"
domain="[('count_pending_revisions', '>', 0)]"/>
<filter string="Pending Changesets"
name="pending_changesets"
domain="[('count_pending_changesets', '>', 0)]"/>
</filter> </filter>
</field> </field>
</record> </record>

43
partner_revision/views/revision_field_rule_views.xml

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data noupdate="0"> <data noupdate="0">
<record id="view_revision_field_rule_tree" model="ir.ui.view">
<field name="name">revision.field.rule.tree</field>
<field name="model">revision.field.rule</field>
<record id="view_changeset_field_rule_tree" model="ir.ui.view">
<field name="name">changeset.field.rule.tree</field>
<field name="model">changeset.field.rule</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Revision Fields Rules">
<tree string="Changeset Fields Rules">
<field name="model_id"/> <field name="model_id"/>
<field name="field_id"/> <field name="field_id"/>
<field name="action"/> <field name="action"/>
@ -13,12 +13,12 @@
</field> </field>
</record> </record>
<record id="view_revision_field_rule_form" model="ir.ui.view">
<field name="name">revision.field.rule.form</field>
<field name="model">revision.field.rule</field>
<record id="view_changeset_field_rule_form" model="ir.ui.view">
<field name="name">changeset.field.rule.form</field>
<field name="model">changeset.field.rule</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Revision Fields Rules">
<sheet string="Revision Fields Rules">
<form string="Changeset Fields Rules">
<sheet string="Changeset Fields Rules">
<group> <group>
<field name="model_id" invisible="1"/> <field name="model_id" invisible="1"/>
<field name="field_id" <field name="field_id"
@ -32,11 +32,11 @@
</field> </field>
</record> </record>
<record id="view_revision_field_rule_search" model="ir.ui.view">
<field name="name">revision.field.rule.search</field>
<field name="model">revision.field.rule</field>
<record id="view_changeset_field_rule_search" model="ir.ui.view">
<field name="name">changeset.field.rule.search</field>
<field name="model">changeset.field.rule</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Revision Fields Rules">
<search string="Changeset Fields Rules">
<field name="model_id"/> <field name="model_id"/>
<field name="field_id"/> <field name="field_id"/>
<field name="action"/> <field name="action"/>
@ -44,19 +44,20 @@
</field> </field>
</record> </record>
<record model="ir.actions.act_window" id="action_revision_field_rule_view">
<field name="name">Revision Fields Rules</field>
<record model="ir.actions.act_window" id="action_changeset_field_rule_view">
<field name="name">Changeset Fields Rules</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">revision.field.rule</field>
<field name="res_model">changeset.field.rule</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_revision_field_rule_search"/>
<field name="search_view_id" ref="view_changeset_field_rule_search"/>
</record> </record>
<menuitem id="menu_revision_field_rule"
parent="menu_revision"
groups="group_revision_manager"
<menuitem id="menu_changeset_field_rule"
parent="menu_changeset"
name="Field Rules"
groups="group_changeset_manager"
sequence="20" sequence="20"
action="action_revision_field_rule_view"/>
action="action_changeset_field_rule_view"/>
</data> </data>
</openerp> </openerp>
Loading…
Cancel
Save