Track record changesets

base_changeset
REPOSITORY
REPOSITORYOCA/server-tools
GIT
GIThttps://github.com/OCA/server-tools.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/server-tools/tree/16.0/base_changeset
VERSION
VERSION 1.0.0
CATEGORY
CATEGORYTools
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Onestein
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Onestein
COMMITTERS
COMMITTERSStefan Rijnhart, Weblate, OCA-git-bot, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/server-tools
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:53:53
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - web
    - base
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (9)
XML IDNameModelTypeStatus
view_changeset_field_rule_form changeset.field.rule form New
view_changeset_field_rule_search changeset.field.rule search New
view_changeset_field_rule_tree changeset.field.rule tree New
view_record_changeset_change_form record.changeset.change form New
view_record_changeset_change_search record.changeset.change search New
view_record_changeset_change_tree record.changeset.change tree New
view_record_changeset_form record.changeset form New
view_record_changeset_search record.changeset search New
view_record_changeset_tree record.changeset tree New
Models touched (4)

New fields (6)
  • changeset_change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' compute='_compute_changeset_ids' string='Changeset Changes'
  • changeset_ids One2many → record.changeset
    comodel_name='record.changeset' compute='_compute_changeset_ids' string='Changesets'
  • count_changesets Integer
    compute='_compute_count_pending_changesets' help='The overall number of changesets of this record'
  • count_pending_changeset_changes Integer
    compute='_compute_count_pending_changesets' help='The number of pending changes of this record'
  • count_pending_changesets Integer
    compute='_compute_count_pending_changesets' help='The number of pending changesets of this record'
  • user_can_see_changeset Boolean
    compute='_compute_user_can_see_changeset'
Public methods (5)
  • action_record_changeset_change_view(self)
  • create(self, vals_list)
    @api.model_create_multi
  • get_view(self, view_id=None, view_type='form', **options)
    @api.model
    Insert the pending changes smart button in the form view of tracked models.
  • models_to_track_changeset(self)
    @api.model@ormcache(skiparg=1)
    Models to be tracked for changes :args: :returns: list of models
  • write(self, values)

New fields (9)
  • action Selection
    help='Auto: always apply a change.\nValidate: manually applied by an administrator.\nNever: change never applied.' required=True selection='_selection_action'
  • active Boolean
    default=True
  • company_id Many2one → res.company
    default=<expr> args: 'res.company'
  • expression Text
    help='Use this rule only on records where this is true. Available variables: object, user'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' required=True
  • model_id Many2one
    related='field_id.model_id' store=True
  • prevent_self_validation Boolean
    default=False
  • source_model_id Many2one → ir.model
    comodel_name='ir.model' domain=<expr> help='If a source model is defined, the rule will be applied only when the change is made from this origin. Rules without source model are global and applies to all backends.\nRules with a source model have precedence over global rules, but if a field has no rule with a source model, the global rule is used.' ondelete='cascade' string='Source Model'
  • validator_group_ids Many2many → res.groups
    default=<expr> string='Validator Groups' args: 'res.groups', 'changeset_field_rule_validator_group_rel'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
  • get_rules(self, source_model_name, record_model_name)
    @api.model
    Return the rules for a model When a model is specified, it will return the rules for this model. Fields that have no rule for this model will use the global rules (those without source). The source model is the model which ask for a change, it will be for instance ``res.users``, ``lefac.backend`` or ``magellan.backend``. The second argument (``source_model_name``) is optional but cannot be an optional keyword argument otherwise it would not be in the key for the cache. The callers have to pass ``None`` if they want only global rules.
  • init(self)
    Ensure there is at most one rule with source_model_id NULL.
  • unlink(self)
  • write(self, vals)

New fields (10)
  • change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' inverse_name='changeset_id' readonly=True string='Changes'
  • company_id Many2one → res.company
    args: 'res.company'
  • date Datetime
    default=fields.Datetime.now() index=True readonly=True string='Modified on'
  • model Char
    index=True readonly=True required=True
  • modified_by_id Many2one → res.users
    default=<expr> readonly=True args: 'res.users'
  • note Text
  • record_id Reference
    compute='_compute_resource_record' readonly=True selection='_reference_models'
  • res_id Many2oneReference
    index=True model_field='model' readonly=True required=True string='Record ID'
  • source Reference
    readonly=True selection='_reference_models' string='Source of the change'
  • state Selection
    compute='_compute_state' selection=[('draft', 'Pending'), ('done', 'Done')] store=True
Public methods (4)
  • add_changeset(self, record, values, create=False)
    @api.model
    Add a changeset on a record By default, when a record is modified by a user or by the system, the the changeset will follow the rules configured for the global rules. A caller should pass the following keys in the context: * ``__changeset_rules_source_model``: name of the model which asks for the change * ``__changeset_rules_source_id``: id of the record which asks for the change When the source model and id are not defined, the current user is considered as the origin of the change. 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 record :type values: dict :param create: in create mode, no check is made to see if the field value consitutes a change. :type creatie: boolean :returns: dict of values that should be wrote on the record (fields with a 'Validate' or 'Never' rule are excluded)
  • apply(self)
  • cancel(self)
  • name_get(self)

New fields (42)
  • changeset_id Many2one → record.changeset
    comodel_name='record.changeset' ondelete='cascade' readonly=True required=True
  • date Datetime
    related='changeset_id.date'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' readonly=True required=True
  • field_name Char
    readonly=True related='field_id.name'
  • field_type Selection
    readonly=True related='field_id.ttype'
  • model Char
    readonly=True related='field_id.model' store=True
  • modified_by_id Many2one
    related='changeset_id.modified_by_id'
  • new_value_boolean Boolean
    readonly=True
  • new_value_char Char
    readonly=True
  • new_value_date Date
    readonly=True
  • new_value_datetime Datetime
    readonly=True
  • new_value_display Char
    compute='_compute_value_display' string='New'
  • new_value_float Float
    readonly=True
  • new_value_integer Integer
    readonly=True
  • new_value_monetary Float
    readonly=True
  • new_value_reference Reference
    readonly=True selection='_reference_models'
  • new_value_text Text
    readonly=True
  • old_value_boolean Boolean
    readonly=True
  • old_value_char Char
    readonly=True
  • old_value_date Date
    readonly=True
  • old_value_datetime Datetime
    readonly=True
  • old_value_float Float
    readonly=True
  • old_value_integer Integer
    readonly=True
  • old_value_monetary Float
    readonly=True
  • old_value_reference Reference
    readonly=True selection='_reference_models'
  • old_value_text Text
    readonly=True
  • origin_value_boolean Boolean
    compute='_compute_origin_values' readonly=True
  • origin_value_char Char
    compute='_compute_origin_values' readonly=True
  • origin_value_date Date
    compute='_compute_origin_values' readonly=True
  • origin_value_datetime Datetime
    compute='_compute_origin_values' readonly=True
  • origin_value_display Char
    compute='_compute_value_display' string='Previous'
  • origin_value_float Float
    compute='_compute_origin_values' readonly=True
  • origin_value_integer Integer
    compute='_compute_origin_values' readonly=True
  • origin_value_monetary Float
    compute='_compute_origin_values' readonly=True
  • origin_value_reference Reference
    compute='_compute_origin_values' readonly=True selection='_reference_models'
  • origin_value_text Text
    compute='_compute_origin_values' readonly=True
  • record_id Reference
    related='changeset_id.record_id'
  • rule_id Many2one → changeset.field.rule
    readonly=True args: 'changeset.field.rule'
  • state Selection
    default='draft' readonly=True required=True selection=[('draft', 'Pending'), ('done', 'Approved'), ('cancel', 'Rejected')]
  • user_can_validate_changeset Boolean
    compute='_compute_user_can_validate_changeset'
  • verified_by_id Many2one → res.users
    readonly=True args: 'res.users'
  • verified_on_date Datetime
    readonly=True string='Verified on'
Public methods (8)
  • apply(self)
    Apply the change on the changeset's record It is optimized thus that it makes only one write on the record per changeset if many changes are applied at once.
  • cancel(self)
    Reject the change
  • get_changeset_changes_by_field(self, model, res_id)
    @api.model
    Return changes grouped by field. :returns: dictionary with field names as keys and lists of dictionaries describing changes as keys. :rtype: dict
  • get_field_for_type(self, field, prefix)
    @api.model
  • get_new_value(self)
  • get_origin_value(self)
  • get_pending_changes_states(self)
    @api.model
  • set_old_value(self)
    Copy the value of the record to the 'old' field
REPOSITORY
REPOSITORYOCA/server-tools
GIT
GIThttps://github.com/OCA/server-tools.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/server-tools/tree/15.0/base_changeset
VERSION
VERSION 1.0.1
CATEGORY
CATEGORYTools
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Onestein
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Onestein
COMMITTERS
COMMITTERSStefan Rijnhart, Miquel Raïch, Weblate, OCA-git-bot, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/server-tools
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:46:39
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - web
    - base
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (9)
XML IDNameModelTypeStatus
view_changeset_field_rule_form changeset.field.rule form New
view_changeset_field_rule_search changeset.field.rule search New
view_changeset_field_rule_tree changeset.field.rule tree New
view_record_changeset_change_form record.changeset.change form New
view_record_changeset_change_search record.changeset.change search New
view_record_changeset_change_tree record.changeset.change tree New
view_record_changeset_form record.changeset form New
view_record_changeset_search record.changeset search New
view_record_changeset_tree record.changeset tree New
Models touched (4)

New fields (5)
  • changeset_change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' compute='_compute_changeset_ids' string='Changeset Changes'
  • changeset_ids One2many → record.changeset
    comodel_name='record.changeset' compute='_compute_changeset_ids' string='Changesets'
  • count_pending_changeset_changes Integer
    compute='_compute_count_pending_changesets'
  • count_pending_changesets Integer
    compute='_compute_count_pending_changesets'
  • user_can_see_changeset Boolean
    compute='_compute_user_can_see_changeset'
Public methods (4)
  • action_record_changeset_change_view(self)
  • create(self, vals_list)
    @api.model_create_multi
  • models_to_track_changeset(self)
    @api.model@ormcache(skiparg=1)
    Models to be tracked for changes :args: :returns: list of models
  • write(self, values)

New fields (9)
  • action Selection
    help='Auto: always apply a change.\nValidate: manually applied by an administrator.\nNever: change never applied.' required=True selection='_selection_action'
  • active Boolean
    default=True
  • company_id Many2one → res.company
    default=<expr> args: 'res.company'
  • expression Text
    help='Use this rule only on records where this is true. Available variables: object, user'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' required=True
  • model_id Many2one
    related='field_id.model_id' store=True
  • prevent_self_validation Boolean
    default=False
  • source_model_id Many2one → ir.model
    comodel_name='ir.model' domain=<expr> help='If a source model is defined, the rule will be applied only when the change is made from this origin. Rules without source model are global and applies to all backends.\nRules with a source model have precedence over global rules, but if a field has no rule with a source model, the global rule is used.' ondelete='cascade' string='Source Model'
  • validator_group_ids Many2many → res.groups
    default=<expr> string='Validator Groups' args: 'res.groups', 'changeset_field_rule_validator_group_rel'
Public methods (5)
  • create(self, vals)
    @api.model
  • get_rules(self, source_model_name, record_model_name)
    @api.model
    Return the rules for a model When a model is specified, it will return the rules for this model. Fields that have no rule for this model will use the global rules (those without source). The source model is the model which ask for a change, it will be for instance ``res.users``, ``lefac.backend`` or ``magellan.backend``. The second argument (``source_model_name``) is optional but cannot be an optional keyword argument otherwise it would not be in the key for the cache. The callers have to pass ``None`` if they want only global rules.
  • init(self)
    Ensure there is at most one rule with source_model_id NULL.
  • unlink(self)
  • write(self, vals)

New fields (10)
  • change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' inverse_name='changeset_id' readonly=True string='Changes'
  • company_id Many2one → res.company
    args: 'res.company'
  • date Datetime
    default=fields.Datetime.now() index=True readonly=True string='Modified on'
  • model Char
    index=True readonly=True required=True
  • modified_by_id Many2one → res.users
    default=<expr> readonly=True args: 'res.users'
  • note Text
  • record_id Reference
    compute='_compute_resource_record' readonly=True selection='_reference_models'
  • res_id Many2oneReference
    index=True model_field='model' readonly=True required=True string='Record ID'
  • source Reference
    readonly=True selection='_reference_models' string='Source of the change'
  • state Selection
    compute='_compute_state' selection=[('draft', 'Pending'), ('done', 'Done')] store=True
Public methods (4)
  • add_changeset(self, record, values, create=False)
    @api.model
    Add a changeset on a record By default, when a record is modified by a user or by the system, the the changeset will follow the rules configured for the global rules. A caller should pass the following keys in the context: * ``__changeset_rules_source_model``: name of the model which asks for the change * ``__changeset_rules_source_id``: id of the record which asks for the change When the source model and id are not defined, the current user is considered as the origin of the change. 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 record :type values: dict :param create: in create mode, no check is made to see if the field value consitutes a change. :type creatie: boolean :returns: dict of values that should be wrote on the record (fields with a 'Validate' or 'Never' rule are excluded)
  • apply(self)
  • cancel(self)
  • name_get(self)

New fields (42)
  • changeset_id Many2one → record.changeset
    comodel_name='record.changeset' ondelete='cascade' readonly=True required=True
  • date Datetime
    related='changeset_id.date'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' readonly=True required=True
  • field_name Char
    readonly=True related='field_id.name'
  • field_type Selection
    readonly=True related='field_id.ttype'
  • model Char
    readonly=True related='field_id.model' store=True
  • modified_by_id Many2one
    related='changeset_id.modified_by_id'
  • new_value_boolean Boolean
    readonly=True
  • new_value_char Char
    readonly=True
  • new_value_date Date
    readonly=True
  • new_value_datetime Datetime
    readonly=True
  • new_value_display Char
    compute='_compute_value_display' string='New'
  • new_value_float Float
    readonly=True
  • new_value_integer Integer
    readonly=True
  • new_value_monetary Float
    readonly=True
  • new_value_reference Reference
    readonly=True selection='_reference_models'
  • new_value_text Text
    readonly=True
  • old_value_boolean Boolean
    readonly=True
  • old_value_char Char
    readonly=True
  • old_value_date Date
    readonly=True
  • old_value_datetime Datetime
    readonly=True
  • old_value_float Float
    readonly=True
  • old_value_integer Integer
    readonly=True
  • old_value_monetary Float
    readonly=True
  • old_value_reference Reference
    readonly=True selection='_reference_models'
  • old_value_text Text
    readonly=True
  • origin_value_boolean Boolean
    compute='_compute_origin_values' readonly=True
  • origin_value_char Char
    compute='_compute_origin_values' readonly=True
  • origin_value_date Date
    compute='_compute_origin_values' readonly=True
  • origin_value_datetime Datetime
    compute='_compute_origin_values' readonly=True
  • origin_value_display Char
    compute='_compute_value_display' string='Previous'
  • origin_value_float Float
    compute='_compute_origin_values' readonly=True
  • origin_value_integer Integer
    compute='_compute_origin_values' readonly=True
  • origin_value_monetary Float
    compute='_compute_origin_values' readonly=True
  • origin_value_reference Reference
    compute='_compute_origin_values' readonly=True selection='_reference_models'
  • origin_value_text Text
    compute='_compute_origin_values' readonly=True
  • record_id Reference
    related='changeset_id.record_id'
  • rule_id Many2one → changeset.field.rule
    readonly=True args: 'changeset.field.rule'
  • state Selection
    default='draft' readonly=True required=True selection=[('draft', 'Pending'), ('done', 'Approved'), ('cancel', 'Rejected')]
  • user_can_validate_changeset Boolean
    compute='_compute_user_can_validate_changeset'
  • verified_by_id Many2one → res.users
    readonly=True args: 'res.users'
  • verified_on_date Datetime
    readonly=True string='Verified on'
Public methods (8)
  • apply(self)
    Apply the change on the changeset's record It is optimized thus that it makes only one write on the record per changeset if many changes are applied at once.
  • cancel(self)
    Reject the change
  • get_field_for_type(self, field, prefix)
    @api.model
  • get_fields_changeset_changes(self, model, res_id)
    @api.model
  • get_new_value(self)
  • get_origin_value(self)
  • get_pending_changes_states(self)
    @api.model
  • set_old_value(self)
    Copy the value of the record to the 'old' field
REPOSITORY
REPOSITORYOCA/server-tools
GIT
GIThttps://github.com/OCA/server-tools.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/server-tools/tree/14.0/base_changeset
VERSION
VERSION 2.0.2
CATEGORY
CATEGORYTools
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Onestein
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Onestein
COMMITTERS
COMMITTERSStefan Rijnhart, Sylvain LE GAL, OCA Transbot, oca-travis, Weblate, OCA-git-bot, oca-ci, dsolanki, oca-git-bot, shams-emesa
WEBSITE
WEBSITEhttps://github.com/OCA/server-tools
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:41:06
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - web
    - base
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (10)
XML IDNameModelTypeStatus
assets_backend assets_backend ir.ui.view qweb Inherits web.assets_backend
view_changeset_field_rule_form changeset.field.rule form New
view_changeset_field_rule_search changeset.field.rule search New
view_changeset_field_rule_tree changeset.field.rule tree New
view_record_changeset_change_form record.changeset.change form New
view_record_changeset_change_search record.changeset.change search New
view_record_changeset_change_tree record.changeset.change tree New
view_record_changeset_form record.changeset form New
view_record_changeset_search record.changeset search New
view_record_changeset_tree record.changeset tree New
Models touched (4)

New fields (5)
  • changeset_change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' compute='_compute_changeset_ids' string='Changeset Changes'
  • changeset_ids One2many → record.changeset
    comodel_name='record.changeset' compute='_compute_changeset_ids' string='Changesets'
  • count_pending_changeset_changes Integer
    compute='_compute_count_pending_changesets'
  • count_pending_changesets Integer
    compute='_compute_count_pending_changesets'
  • user_can_see_changeset Boolean
    compute='_compute_user_can_see_changeset'
Public methods (4)
  • action_record_changeset_change_view(self)
  • create(self, vals_list)
    @api.model_create_multi
  • models_to_track_changeset(self)
    @api.model@ormcache(skiparg=1)
    Models to be tracked for changes :args: :returns: list of models
  • write(self, values)

New fields (9)
  • action Selection
    help='Auto: always apply a change.\nValidate: manually applied by an administrator.\nNever: change never applied.' required=True selection='_selection_action' string='Action'
  • active Boolean
    default=True
  • company_id Many2one → res.company
    default=<expr> args: 'res.company'
  • expression Text
    help='Use this rule only on records where this is true. Available variables: object, user'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' required=True
  • model_id Many2one
    related='field_id.model_id' store=True
  • prevent_self_validation Boolean
    default=False
  • source_model_id Many2one → ir.model
    comodel_name='ir.model' domain=<expr> help='If a source model is defined, the rule will be applied only when the change is made from this origin. Rules without source model are global and applies to all backends.\nRules with a source model have precedence over global rules, but if a field has no rule with a source model, the global rule is used.' ondelete='cascade' string='Source Model'
  • validator_group_ids Many2many → res.groups
    default=<expr> string='Validator Groups' args: 'res.groups', 'changeset_field_rule_validator_group_rel'
Public methods (5)
  • create(self, vals)
    @api.model
  • get_rules(self, source_model_name, record_model_name)
    @api.model
    Return the rules for a model When a model is specified, it will return the rules for this model. Fields that have no rule for this model will use the global rules (those without source). The source model is the model which ask for a change, it will be for instance ``res.users``, ``lefac.backend`` or ``magellan.backend``. The second argument (``source_model_name``) is optional but cannot be an optional keyword argument otherwise it would not be in the key for the cache. The callers have to pass ``None`` if they want only global rules.
  • init(self)
    Ensure there is at most one rule with source_model_id NULL.
  • unlink(self)
  • write(self, vals)

New fields (10)
  • change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' inverse_name='changeset_id' readonly=True string='Changes'
  • company_id Many2one → res.company
    args: 'res.company'
  • date Datetime
    default=fields.Datetime.now() index=True readonly=True string='Modified on'
  • model Char
    index=True readonly=True required=True
  • modified_by_id Many2one → res.users
    default=<expr> readonly=True args: 'res.users'
  • note Text
  • record_id Reference
    compute='_compute_resource_record' readonly=True selection='_reference_models'
  • res_id Many2oneReference
    index=True model_field='model' readonly=True required=True string='Record ID'
  • source Reference
    readonly=True selection='_reference_models' string='Source of the change'
  • state Selection
    compute='_compute_state' selection=[('draft', 'Pending'), ('done', 'Done')] store=True
Public methods (4)
  • add_changeset(self, record, values, create=False)
    @api.model
    Add a changeset on a record By default, when a record is modified by a user or by the system, the the changeset will follow the rules configured for the global rules. A caller should pass the following keys in the context: * ``__changeset_rules_source_model``: name of the model which asks for the change * ``__changeset_rules_source_id``: id of the record which asks for the change When the source model and id are not defined, the current user is considered as the origin of the change. 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 record :type values: dict :param create: in create mode, no check is made to see if the field value consitutes a change. :type creatie: boolean :returns: dict of values that should be wrote on the record (fields with a 'Validate' or 'Never' rule are excluded)
  • apply(self)
  • cancel(self)
  • name_get(self)

New fields (42)
  • changeset_id Many2one → record.changeset
    comodel_name='record.changeset' ondelete='cascade' readonly=True required=True
  • date Datetime
    related='changeset_id.date'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' readonly=True required=True
  • field_name Char
    readonly=True related='field_id.name'
  • field_type Selection
    readonly=True related='field_id.ttype'
  • model Char
    readonly=True related='field_id.model' store=True
  • modified_by_id Many2one
    related='changeset_id.modified_by_id'
  • new_value_boolean Boolean
    readonly=True
  • new_value_char Char
    readonly=True
  • new_value_date Date
    readonly=True
  • new_value_datetime Datetime
    readonly=True
  • new_value_display Char
    compute='_compute_value_display' string='New'
  • new_value_float Float
    readonly=True
  • new_value_integer Integer
    readonly=True
  • new_value_monetary Float
    readonly=True
  • new_value_reference Reference
    readonly=True selection='_reference_models'
  • new_value_text Text
    readonly=True
  • old_value_boolean Boolean
    readonly=True
  • old_value_char Char
    readonly=True
  • old_value_date Date
    readonly=True
  • old_value_datetime Datetime
    readonly=True
  • old_value_float Float
    readonly=True
  • old_value_integer Integer
    readonly=True
  • old_value_monetary Float
    readonly=True
  • old_value_reference Reference
    readonly=True selection='_reference_models'
  • old_value_text Text
    readonly=True
  • origin_value_boolean Boolean
    compute='_compute_origin_values' readonly=True
  • origin_value_char Char
    compute='_compute_origin_values' readonly=True
  • origin_value_date Date
    compute='_compute_origin_values' readonly=True
  • origin_value_datetime Datetime
    compute='_compute_origin_values' readonly=True
  • origin_value_display Char
    compute='_compute_value_display' string='Previous'
  • origin_value_float Float
    compute='_compute_origin_values' readonly=True
  • origin_value_integer Integer
    compute='_compute_origin_values' readonly=True
  • origin_value_monetary Float
    compute='_compute_origin_values' readonly=True
  • origin_value_reference Reference
    compute='_compute_origin_values' readonly=True selection='_reference_models'
  • origin_value_text Text
    compute='_compute_origin_values' readonly=True
  • record_id Reference
    related='changeset_id.record_id'
  • rule_id Many2one → changeset.field.rule
    readonly=True args: 'changeset.field.rule'
  • state Selection
    default='draft' readonly=True required=True selection=[('draft', 'Pending'), ('done', 'Approved'), ('cancel', 'Rejected')]
  • user_can_validate_changeset Boolean
    compute='_compute_user_can_validate_changeset'
  • verified_by_id Many2one → res.users
    readonly=True args: 'res.users'
  • verified_on_date Datetime
    readonly=True string='Verified on'
Public methods (8)
  • apply(self)
    Apply the change on the changeset's record It is optimized thus that it makes only one write on the record per changeset if many changes are applied at once.
  • cancel(self)
    Reject the change
  • get_field_for_type(self, field, prefix)
    @api.model
  • get_fields_changeset_changes(self, model, res_id)
    @api.model
  • get_new_value(self)
  • get_origin_value(self)
  • get_pending_changes_states(self)
    @api.model
  • set_old_value(self)
    Copy the value of the record to the 'old' field
REPOSITORY
REPOSITORYOCA/server-tools
GIT
GIThttps://github.com/OCA/server-tools.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/server-tools/tree/13.0/base_changeset
VERSION
VERSION 1.0.1
CATEGORY
CATEGORYTools
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Onestein
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Onestein
COMMITTERS
COMMITTERSStefan Rijnhart, Andrea, OCA Transbot, oca-travis, Weblate, OCA-git-bot, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/server-tools
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:34:17
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - web
    - base
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (10)
XML IDNameModelTypeStatus
assets_backend assets_backend ir.ui.view qweb Inherits web.assets_backend
view_changeset_field_rule_form changeset.field.rule form New
view_changeset_field_rule_search changeset.field.rule search New
view_changeset_field_rule_tree changeset.field.rule tree New
view_record_changeset_change_form record.changeset.change form New
view_record_changeset_change_search record.changeset.change search New
view_record_changeset_change_tree record.changeset.change tree New
view_record_changeset_form record.changeset form New
view_record_changeset_search record.changeset search New
view_record_changeset_tree record.changeset tree New
Models touched (4)

New fields (5)
  • changeset_change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' compute='_compute_changeset_ids' string='Changeset Changes'
  • changeset_ids One2many → record.changeset
    comodel_name='record.changeset' compute='_compute_changeset_ids' string='Changesets'
  • count_pending_changeset_changes Integer
    compute='_compute_count_pending_changesets'
  • count_pending_changesets Integer
    compute='_compute_count_pending_changesets'
  • user_can_see_changeset Boolean
    compute='_compute_user_can_see_changeset'
Public methods (3)
  • action_record_changeset_change_view(self)
  • models_to_track_changeset(self)
    @api.model
    Models to be tracked for changes :args: :returns: list of models
  • write(self, values)

New fields (6)
  • action Selection
    help='Auto: always apply a change.\nValidate: manually applied by an administrator.\nNever: change never applied.' required=True selection='_selection_action' string='Action'
  • active Boolean
    default=True
  • company_id Many2one → res.company
    default=<expr> args: 'res.company'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' required=True
  • model_id Many2one
    related='field_id.model_id' store=True
  • source_model_id Many2one → ir.model
    comodel_name='ir.model' domain=<expr> help='If a source model is defined, the rule will be applied only when the change is made from this origin. Rules without source model are global and applies to all backends.\nRules with a source model have precedence over global rules, but if a field has no rule with a source model, the global rule is used.' ondelete='cascade' string='Source Model'
Public methods (5)
  • create(self, vals)
    @api.model
  • get_rules(self, source_model_name, record_model_name)
    @api.model
    Return the rules for a model When a model is specified, it will return the rules for this model. Fields that have no rule for this model will use the global rules (those without source). The source model is the model which ask for a change, it will be for instance ``res.users``, ``lefac.backend`` or ``magellan.backend``. The second argument (``source_model_name``) is optional but cannot be an optional keyword argument otherwise it would not be in the key for the cache. The callers have to pass ``None`` if they want only global rules.
  • init(self)
    Ensure there is at most one rule with source_model_id NULL.
  • unlink(self)
  • write(self, vals)

New fields (10)
  • change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' inverse_name='changeset_id' readonly=True string='Changes'
  • company_id Many2one → res.company
    args: 'res.company'
  • date Datetime
    default=fields.Datetime.now() index=True readonly=True string='Modified on'
  • model Char
    index=True readonly=True required=True
  • modified_by_id Many2one → res.users
    default=<expr> readonly=True args: 'res.users'
  • note Text
  • record_id Reference
    compute='_compute_resource_record' readonly=True selection='_reference_models'
  • res_id Many2oneReference
    index=True model_field='model' readonly=True required=True string='Record ID'
  • source Reference
    readonly=True selection='_reference_models' string='Source of the change'
  • state Selection
    compute='_compute_state' selection=[('draft', 'Pending'), ('done', 'Done')] store=True
Public methods (4)
  • add_changeset(self, record, values)
    @api.model
    Add a changeset on a record By default, when a record is modified by a user or by the system, the the changeset will follow the rules configured for the global rules. A caller should pass the following keys in the context: * ``__changeset_rules_source_model``: name of the model which asks for the change * ``__changeset_rules_source_id``: id of the record which asks for the change When the source model and id are not defined, the current user is considered as the origin of the change. 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 record :type values: dict :returns: dict of values that should be wrote on the record (fields with a 'Validate' or 'Never' rule are excluded)
  • apply(self)
  • cancel(self)
  • name_get(self)

New fields (42)
  • changeset_id Many2one → record.changeset
    comodel_name='record.changeset' ondelete='cascade' readonly=True required=True
  • date Datetime
    related='changeset_id.date'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' readonly=True required=True
  • field_name Char
    readonly=True related='field_id.name'
  • field_type Selection
    readonly=True related='field_id.ttype'
  • model Char
    readonly=True related='field_id.model' store=True
  • modified_by_id Many2one
    related='changeset_id.modified_by_id'
  • new_value_boolean Boolean
    readonly=True
  • new_value_char Char
    readonly=True
  • new_value_date Date
    readonly=True
  • new_value_datetime Datetime
    readonly=True
  • new_value_display Char
    compute='_compute_value_display' string='New'
  • new_value_float Float
    readonly=True
  • new_value_integer Integer
    readonly=True
  • new_value_monetary Float
    readonly=True
  • new_value_reference Reference
    readonly=True selection='_reference_models'
  • new_value_text Text
    readonly=True
  • old_value_boolean Boolean
    readonly=True
  • old_value_char Char
    readonly=True
  • old_value_date Date
    readonly=True
  • old_value_datetime Datetime
    readonly=True
  • old_value_float Float
    readonly=True
  • old_value_integer Integer
    readonly=True
  • old_value_monetary Float
    readonly=True
  • old_value_reference Reference
    readonly=True selection='_reference_models'
  • old_value_text Text
    readonly=True
  • origin_value_boolean Boolean
    compute='_compute_origin_values' readonly=True
  • origin_value_char Char
    compute='_compute_origin_values' readonly=True
  • origin_value_date Date
    compute='_compute_origin_values' readonly=True
  • origin_value_datetime Datetime
    compute='_compute_origin_values' readonly=True
  • origin_value_display Char
    compute='_compute_value_display' string='Previous'
  • origin_value_float Float
    compute='_compute_origin_values' readonly=True
  • origin_value_integer Integer
    compute='_compute_origin_values' readonly=True
  • origin_value_monetary Float
    compute='_compute_origin_values' readonly=True
  • origin_value_reference Reference
    compute='_compute_origin_values' readonly=True selection='_reference_models'
  • origin_value_text Text
    compute='_compute_origin_values' readonly=True
  • record_id Reference
    related='changeset_id.record_id'
  • rule_id Many2one → changeset.field.rule
    readonly=True args: 'changeset.field.rule'
  • state Selection
    default='draft' readonly=True required=True selection=[('draft', 'Pending'), ('done', 'Approved'), ('cancel', 'Rejected')]
  • user_can_validate_changeset Boolean
    compute='_compute_user_can_validate_changeset'
  • verified_by_id Many2one → res.users
    readonly=True args: 'res.users'
  • verified_on_date Datetime
    readonly=True string='Verified on'
Public methods (8)
  • apply(self)
    Apply the change on the changeset's record It is optimized thus that it makes only one write on the record per changeset if many changes are applied at once.
  • cancel(self)
    Reject the change
  • get_field_for_type(self, field, prefix)
    @api.model
  • get_fields_changeset_changes(self, model, res_id)
    @api.model
  • get_new_value(self)
  • get_origin_value(self)
  • get_pending_changes_states(self)
    @api.model
  • set_old_value(self)
    Copy the value of the record to the 'old' field
REPOSITORY
REPOSITORYOCA/server-tools
GIT
GIThttps://github.com/OCA/server-tools.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/server-tools/tree/12.0/base_changeset
VERSION
VERSION 1.1.2
CATEGORY
CATEGORYTools
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Onestein
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Onestein
COMMITTERS
COMMITTERSHolger Brunn, OCA Transbot, oca-travis, Weblate, OCA-git-bot, Tom Blauwendraat, oca-ci, Dariusz Kubiak
WEBSITE
WEBSITEhttps://github.com/OCA/server-tools
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:29:22
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - web
    - base
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (10)
XML IDNameModelTypeStatus
assets_backend assets_backend ir.ui.view qweb Inherits web.assets_backend
view_changeset_field_rule_form changeset.field.rule form New
view_changeset_field_rule_search changeset.field.rule search New
view_changeset_field_rule_tree changeset.field.rule tree New
view_record_changeset_change_form record.changeset.change form New
view_record_changeset_change_search record.changeset.change search New
view_record_changeset_change_tree record.changeset.change tree New
view_record_changeset_form record.changeset form New
view_record_changeset_search record.changeset search New
view_record_changeset_tree record.changeset tree New
Models touched (4)

New fields (5)
  • changeset_change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' compute='_compute_changeset_ids' string='Changeset Changes'
  • changeset_ids One2many → record.changeset
    comodel_name='record.changeset' compute='_compute_changeset_ids' string='Changesets'
  • count_pending_changeset_changes Integer
    compute='_compute_count_pending_changesets'
  • count_pending_changesets Integer
    compute='_compute_count_pending_changesets'
  • user_can_see_changeset Boolean
    compute='_compute_user_can_see_changeset'
Public methods (4)
  • action_record_changeset_change_view(self)
  • create(self, vals_list)
    @api.model_create_multi
  • models_to_track_changeset(self)
    @api.model
    Models to be tracked for changes :args: :returns: list of models
  • write(self, values)

New fields (8)
  • action Selection
    help='Auto: always apply a change.\nValidate: manually applied by an administrator.\nNever: change never applied.' required=True selection='_selection_action' string='Action'
  • active Boolean
    default=True
  • company_id Many2one → res.company
    default=<expr> args: 'res.company'
  • expression Text
    help='Use this rule only on records where this is true. Available variables: object, user'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' ondelete='cascade' required=True
  • model_id Many2one
    related='field_id.model_id' store=True
  • source_model_id Many2one → ir.model
    comodel_name='ir.model' domain=<expr> help='If a source model is defined, the rule will be applied only when the change is made from this origin. Rules without source model are global and applies to all backends.\nRules with a source model have precedence over global rules, but if a field has no rule with a source model, the global rule is used.' ondelete='cascade' string='Source Model'
  • validator_group_ids Many2many → res.groups
    default=<expr> string='Validator Groups' args: 'res.groups', 'changeset_field_rule_validator_group_rel'
Public methods (5)
  • create(self, vals)
    @api.model
  • get_rules(self, source_model_name, record_model_name)
    @api.model
    Return the rules for a model When a model is specified, it will return the rules for this model. Fields that have no rule for this model will use the global rules (those without source). The source model is the model which ask for a change, it will be for instance ``res.users``, ``lefac.backend`` or ``magellan.backend``. The second argument (``source_model_name``) is optional but cannot be an optional keyword argument otherwise it would not be in the key for the cache. The callers have to pass ``None`` if they want only global rules.
  • init(self)
    Ensure there is at most one rule with source_model_id NULL.
  • unlink(self)
  • write(self, vals)

New fields (10)
  • change_ids One2many → record.changeset.change
    comodel_name='record.changeset.change' inverse_name='changeset_id' readonly=True string='Changes'
  • company_id Many2one → res.company
    args: 'res.company'
  • date Datetime
    default=fields.Datetime.now index=True readonly=True string='Modified on'
  • model Char
    index=True readonly=True required=True
  • modified_by_id Many2one → res.users
    default=<expr> readonly=True args: 'res.users'
  • note Text
  • record_id Reference
    compute='_compute_resource_record' readonly=True selection='_reference_models'
  • res_id Integer
    index=True readonly=True required=True string='Record ID'
  • source Reference
    readonly=True selection='_reference_models' string='Source of the change'
  • state Selection
    compute='_compute_state' selection=[('draft', 'Pending'), ('done', 'Done')] store=True
Public methods (4)
  • add_changeset(self, record, values)
    @api.model
    Add a changeset on a record By default, when a record is modified by a user or by the system, the the changeset will follow the rules configured for the global rules. A caller should pass the following keys in the context: * ``__changeset_rules_source_model``: name of the model which asks for the change * ``__changeset_rules_source_id``: id of the record which asks for the change When the source model and id are not defined, the current user is considered as the origin of the change. 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 record :type values: dict :returns: dict of values that should be wrote on the record (fields with a 'Validate' or 'Never' rule are excluded)
  • apply(self)
  • cancel(self)
  • name_get(self)

New fields (42)
  • changeset_id Many2one → record.changeset
    comodel_name='record.changeset' ondelete='cascade' readonly=True required=True
  • date Datetime
    related='changeset_id.date'
  • field_id Many2one → ir.model.fields
    comodel_name='ir.model.fields' readonly=True required=True
  • field_name Char
    readonly=True related='field_id.name'
  • field_type Selection
    readonly=True related='field_id.ttype'
  • model Char
    readonly=True related='field_id.model' store=True
  • modified_by_id Many2one
    related='changeset_id.modified_by_id'
  • new_value_boolean Boolean
    readonly=True
  • new_value_char Char
    readonly=True
  • new_value_date Date
    readonly=True
  • new_value_datetime Datetime
    readonly=True
  • new_value_display Char
    compute='_compute_value_display' string='New'
  • new_value_float Float
    readonly=True
  • new_value_integer Integer
    readonly=True
  • new_value_monetary Float
    readonly=True
  • new_value_reference Reference
    readonly=True selection='_reference_models'
  • new_value_text Text
    readonly=True
  • old_value_boolean Boolean
    readonly=True
  • old_value_char Char
    readonly=True
  • old_value_date Date
    readonly=True
  • old_value_datetime Datetime
    readonly=True
  • old_value_float Float
    readonly=True
  • old_value_integer Integer
    readonly=True
  • old_value_monetary Float
    readonly=True
  • old_value_reference Reference
    readonly=True selection='_reference_models'
  • old_value_text Text
    readonly=True
  • origin_value_boolean Boolean
    compute='_compute_origin_values' readonly=True
  • origin_value_char Char
    compute='_compute_origin_values' readonly=True
  • origin_value_date Date
    compute='_compute_origin_values' readonly=True
  • origin_value_datetime Datetime
    compute='_compute_origin_values' readonly=True
  • origin_value_display Char
    compute='_compute_value_display' string='Previous'
  • origin_value_float Float
    compute='_compute_origin_values' readonly=True
  • origin_value_integer Integer
    compute='_compute_origin_values' readonly=True
  • origin_value_monetary Float
    compute='_compute_origin_values' readonly=True
  • origin_value_reference Reference
    compute='_compute_origin_values' readonly=True selection='_reference_models'
  • origin_value_text Text
    compute='_compute_origin_values' readonly=True
  • record_id Reference
    related='changeset_id.record_id'
  • rule_id Many2one → changeset.field.rule
    readonly=True args: 'changeset.field.rule'
  • state Selection
    default='draft' readonly=True required=True selection=[('draft', 'Pending'), ('done', 'Approved'), ('cancel', 'Rejected')]
  • user_can_validate_changeset Boolean
    compute='_compute_user_can_validate_changeset'
  • verified_by_id Many2one → res.users
    readonly=True args: 'res.users'
  • verified_on_date Datetime
    readonly=True string='Verified on'
Public methods (8)
  • apply(self)
    Apply the change on the changeset's record It is optimized thus that it makes only one write on the record per changeset if many changes are applied at once.
  • cancel(self)
    Reject the change
  • get_field_for_type(self, field, prefix)
    @api.model
  • get_fields_changeset_changes(self, model, res_id)
    @api.model
  • get_new_value(self)
  • get_origin_value(self)
  • get_pending_changes_states(self)
    @api.model
  • set_old_value(self)
    Copy the value of the record to the 'old' field