diff --git a/partner_revision/models/res_partner.py b/partner_revision/models/res_partner.py index 76ce1416a..d7c06d8e2 100644 --- a/partner_revision/models/res_partner.py +++ b/partner_revision/models/res_partner.py @@ -19,7 +19,7 @@ # # -from openerp import models, fields, api +from openerp import models, api class ResPartner(models.Model): @@ -30,106 +30,8 @@ class ResPartner(models.Model): if self.env.context.get('__no_revision'): return super(ResPartner, self).write(values) else: + revision_model = self.env['res.partner.revision'] for record in self: - values = record._add_revision(values) - super(ResPartner, record).write(values) + local_values = revision_model.add_revision(record, values) + super(ResPartner, record).write(local_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): - """ Add a revision on a partner - - By default, when a partner is modified by a user or by the - system, the changes are applied and a validated revision is - 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. - - :param values: the values being written on the partner - :type values: dict - - :returns: dict of values that should be wrote on the partner - (fields with a 'Validate' or 'Never' rule are excluded) - - """ - self.ensure_one() - write_values = values.copy() - changes = [] - rules = self.env['revision.behavior'].get_rules(self._model._name) - for field in values: - rule = rules.get(field) - if not rule: - continue - if field in values: - if not self._has_field_changed(field, values[field]): - continue - 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({ - 'partner_id': self.id, - 'change_ids': [(0, 0, vals) for vals in changes], - 'date': fields.Datetime.now(), - }) - return write_values diff --git a/partner_revision/models/res_partner_revision.py b/partner_revision/models/res_partner_revision.py index 17a9cf19b..62e4a8286 100644 --- a/partner_revision/models/res_partner_revision.py +++ b/partner_revision/models/res_partner_revision.py @@ -41,6 +41,55 @@ class ResPartnerRevision(models.Model): def apply(self): self.mapped('change_ids').apply() + @api.multi + def add_revision(self, record, values): + """ Add a revision on a partner + + By default, when a partner is modified by a user or by the + system, the changes are applied and a validated revision is + 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. + + Should be called before the execution of ``write`` on the record + so we can keep track of the existing value and also because the + returned values should be used for ``write`` as some of the + values may have been removed. + + :param values: the values being written on the partner + :type values: dict + + :returns: dict of values that should be wrote on the partner + (fields with a 'Validate' or 'Never' rule are excluded) + + """ + record.ensure_one() + change_model = self.env['res.partner.revision.change'] + write_values = values.copy() + changes = [] + rules = self.env['revision.behavior'].get_rules(record._model._name) + for field in values: + rule = rules.get(field) + if not rule: + continue + if field in values: + if not change_model._has_field_changed(record, field, + values[field]): + continue + change, pop_value = change_model._prepare_revision_change( + record, rule, field, values[field] + ) + if pop_value: + write_values.pop(field) + changes.append(change) + if changes: + self.env['res.partner.revision'].create({ + 'partner_id': record.id, + 'change_ids': [(0, 0, vals) for vals in changes], + 'date': fields.Datetime.now(), + }) + return write_values + class ResPartnerRevisionChange(models.Model): _name = 'res.partner.revision.change' @@ -140,12 +189,6 @@ class ResPartnerRevisionChange(models.Model): 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: @@ -156,3 +199,65 @@ class ResPartnerRevisionChange(models.Model): change.get_new_value() ) partner.write({change.field_id.name: value_for_write}) + + @api.model + def _has_field_changed(self, record, field, value): + field_def = record._fields[field] + return field_def.convert_to_write(record[field]) != value + + @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.model + def _convert_value_for_revision(self, record, field, value): + field_def = record._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, record, 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 = record._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(record[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_value_for_revision(record, field, + current_value) + new_value = self._convert_value_for_revision(record, 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 diff --git a/partner_revision/tests/common.py b/partner_revision/tests/common.py index b30beaa62..be8a4d4c2 100644 --- a/partner_revision/tests/common.py +++ b/partner_revision/tests/common.py @@ -74,13 +74,14 @@ class RevisionMixin(object): :param changes: list of changes [(field, new value, state)] :returns: 'res.partner.revision' record """ - get_field = self.env['res.partner.revision.change'].get_field_for_type - convert = self.env['res.partner'].convert_field_for_revision + RevisionChange = self.env['res.partner.revision.change'] + get_field = RevisionChange.get_field_for_type + convert = RevisionChange._convert_value_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) + current_value = convert(partner, field.name, current_value) change = { 'field_id': field.id, # write in the field of the appropriate type for the