Browse Source
Merge pull request #357 from leemannd/9.0-mig-partner_changeset
Merge pull request #357 from leemannd/9.0-mig-partner_changeset
[9.0][Port] mig partner changesetpull/257/merge
Pedro M. Baeza
8 years ago
committed by
GitHub
27 changed files with 3345 additions and 0 deletions
-
168partner_changeset/README.rst
-
4partner_changeset/__init__.py
-
24partner_changeset/__openerp__.py
-
49partner_changeset/demo/changeset_field_rule.xml
-
324partner_changeset/i18n/es.po
-
333partner_changeset/i18n/fr.po
-
289partner_changeset/i18n/partner_changeset.pot
-
331partner_changeset/i18n/sl.po
-
7partner_changeset/models/__init__.py
-
144partner_changeset/models/changeset_field_rule.py
-
50partner_changeset/models/res_partner.py
-
514partner_changeset/models/res_partner_changeset.py
-
10partner_changeset/security/ir.model.access.csv
-
29partner_changeset/security/security.xml
-
BINpartner_changeset/static/description/icon.png
-
BINpartner_changeset/static/src/img/changeset.png
-
BINpartner_changeset/static/src/img/rules.png
-
6partner_changeset/tests/__init__.py
-
77partner_changeset/tests/common.py
-
78partner_changeset/tests/test_changeset_field_rule.py
-
281partner_changeset/tests/test_changeset_field_type.py
-
246partner_changeset/tests/test_changeset_flow.py
-
118partner_changeset/tests/test_changeset_origin.py
-
61partner_changeset/views/changeset_field_rule_views.xml
-
8partner_changeset/views/menu.xml
-
157partner_changeset/views/res_partner_changeset_views.xml
-
37partner_changeset/views/res_partner_views.xml
@ -0,0 +1,168 @@ |
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
================== |
|||
Partner Changesets |
|||
================== |
|||
|
|||
This module extends the functionality of partners. It allows to create |
|||
changesets that must be validated when a partner is modified instead of direct |
|||
modifications. Rules allow to configure which field must be validated. |
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
Access Rights |
|||
------------- |
|||
|
|||
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`` |
|||
|
|||
Changesets Rules |
|||
---------------- |
|||
|
|||
The changesets rules can be configured in ``Sales > Configuration > |
|||
Partner Changesets > Fields Rules``. For each partner field, an |
|||
action can be defined: |
|||
|
|||
* Auto: the changes made on this field are always applied |
|||
* Validate: the changes made on this field must be manually confirmed by |
|||
a 'Changesets User' user |
|||
* Never: the changes made on this field are always refused |
|||
|
|||
In any case, all the changes made by the users are always applied |
|||
directly on the users, but a 'validated' changeset is created for the |
|||
history. |
|||
|
|||
The supported fields are: |
|||
|
|||
* Char |
|||
* Text |
|||
* Date |
|||
* Datetime |
|||
* Integer |
|||
* Float |
|||
* Boolean |
|||
* Many2one |
|||
|
|||
Rules can be global (no source model) or configured by source model. |
|||
Rules by source model have the priority. If a field is not configured |
|||
for the source model, it will use the global rule (if existing). |
|||
|
|||
If a field has no rule, it is written to the partner without changeset. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
General case |
|||
------------ |
|||
|
|||
The first step is to create the changeset rules, once that done, writes on |
|||
partners will be created as changesets. |
|||
|
|||
Finding changesets |
|||
------------------ |
|||
|
|||
A menu lists all the changesets in ``Sales > Configuration > Partner |
|||
Changesets > Changesets``. |
|||
|
|||
However, it is more convenient to access them directly from the |
|||
partners. Pending changesets can be accessed directly from the top right |
|||
of the partners' view. A new filter on the partners shows the partners |
|||
having at least one pending changeset. |
|||
|
|||
Handling changesets |
|||
------------------- |
|||
|
|||
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 |
|||
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 |
|||
the user. |
|||
|
|||
The changes view shows the name of the partner's field, the Origin value |
|||
and the New value alongside the state of the change. By clicking on the |
|||
change in some cases a more detailed view is displayed, for instance, |
|||
links for relations can be clicked on. |
|||
|
|||
A button on a changeset allows to apply or reject all the changes at |
|||
once. |
|||
|
|||
Custom source rules in your addon |
|||
--------------------------------- |
|||
|
|||
Addons wanting to create changeset with their own rules should pass the |
|||
following keys in the context when they write on the partner: |
|||
|
|||
* ``__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 |
|||
|
|||
Also, they should extend the selection in |
|||
``ChangesetFieldRule._domain_source_models`` to add their model (the |
|||
same that is passed in ``__changeset_rules_source_model``). |
|||
|
|||
The source is used for the application of the rules, allowing to have a |
|||
different rule for a different source. It is also stored on the changeset for |
|||
information. |
|||
|
|||
Screenshot: |
|||
----------- |
|||
|
|||
* Configuration of rules |
|||
|
|||
.. image:: partner_changeset/static/src/img/rules.png |
|||
|
|||
* Changeset waiting for validation |
|||
|
|||
.. image:: partner_changeset/static/src/img/changeset.png |
|||
|
|||
|
|||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
|||
:alt: Try me on Runbot |
|||
:target: https://runbot.odoo-community.org/runbot/134/9.0 |
|||
|
|||
Known issues / Roadmap |
|||
====================== |
|||
|
|||
* Only a subset of the type of fields is actually supported |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues |
|||
<https://github.com/OCA/partner-contact/issues>`_. In case of trouble, please |
|||
check there if your issue has already been reported. If you spotted it first, |
|||
help us smashing it by providing a detailed and welcomed `feedback |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Images |
|||
------ |
|||
|
|||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_. |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Guewen Baconnier <guewen.baconnier@camptocamp.com> |
|||
* Denis Leemann <denis.leemann@camptocamp.com> |
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from . import models |
@ -0,0 +1,24 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
{'name': 'Partner Changesets', |
|||
'version': '9.0.1.0.0', |
|||
'author': 'Camptocamp, Odoo Community Association (OCA)', |
|||
'license': 'AGPL-3', |
|||
'category': 'Sales Management', |
|||
'depends': ['base', |
|||
'sale', |
|||
], |
|||
'website': 'http://www.camptocamp.com', |
|||
'data': ['security/security.xml', |
|||
'security/ir.model.access.csv', |
|||
'views/menu.xml', |
|||
'views/res_partner_changeset_views.xml', |
|||
'views/changeset_field_rule_views.xml', |
|||
'views/res_partner_views.xml', |
|||
], |
|||
'demo': ['demo/changeset_field_rule.xml', |
|||
], |
|||
'installable': True, |
|||
} |
@ -0,0 +1,49 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo noupdate="1"> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_name"> |
|||
<field name="field_id" ref="base.field_res_partner_name"/> |
|||
<field name="action">auto</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_street"> |
|||
<field name="field_id" ref="base.field_res_partner_street"/> |
|||
<field name="action">validate</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_street2"> |
|||
<field name="field_id" ref="base.field_res_partner_street2"/> |
|||
<field name="action">validate</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_zip"> |
|||
<field name="field_id" ref="base.field_res_partner_zip"/> |
|||
<field name="action">validate</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_city"> |
|||
<field name="field_id" ref="base.field_res_partner_city"/> |
|||
<field name="action">validate</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_email"> |
|||
<field name="field_id" ref="base.field_res_partner_email"/> |
|||
<field name="action">never</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_ref"> |
|||
<field name="field_id" ref="base.field_res_partner_ref"/> |
|||
<field name="action">validate</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_country_id"> |
|||
<field name="field_id" ref="base.field_res_partner_country_id"/> |
|||
<field name="action">validate</field> |
|||
</record> |
|||
|
|||
<record model="changeset.field.rule" id="changeset_field_rule_credit_limit"> |
|||
<field name="field_id" ref="base.field_res_partner_credit_limit"/> |
|||
<field name="action">validate</field> |
|||
</record> |
|||
|
|||
</odoo> |
@ -0,0 +1,324 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * partner_changeset |
|||
# |
|||
# Translators: |
|||
# Oihane Crucelaegui <oihanecruce@gmail.com>, 2016 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2016-07-03 07:56+0000\n" |
|||
"PO-Revision-Date: 2016-07-03 07:56+0000\n" |
|||
"Last-Translator: Oihane Crucelaegui <oihanecruce@gmail.com>, 2016\n" |
|||
"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Language: es\n" |
|||
"Plural-Forms: nplurals=2; plural=(n != 1);\n" |
|||
|
|||
#. module: partner_changeset |
|||
#: sql_constraint:changeset.field.rule:0 |
|||
msgid "A rule already exists for this field." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Accepted" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,action:0 |
|||
msgid "Action" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Apply" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Apply pending changes" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,action:0 |
|||
msgid "" |
|||
"Auto: always apply a change.\n" |
|||
"Validate: manually applied by an administrator.\n" |
|||
"Never: change never applied." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner:partner_changeset.res_partner_view_buttons |
|||
#: field:res.partner,count_pending_changesets:0 |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: field:res.partner.changeset,change_ids:0 |
|||
msgid "Changes" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,changeset_id:0 |
|||
msgid "Changeset" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_manager |
|||
msgid "Changeset Configuration" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_changeset_field_rule |
|||
msgid "Changeset Field Rules" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_form |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_search |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_tree |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_changeset_field_rule_view |
|||
msgid "Changeset Fields Rules" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_res_partner_changeset |
|||
#: field:res.partner,changeset_ids:0 |
|||
msgid "Changesets" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_user |
|||
msgid "Changesets Validations " |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_uid:0 |
|||
#: field:res.partner.changeset,create_uid:0 |
|||
#: field:res.partner.changeset.change,create_uid:0 |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_date:0 |
|||
#: field:res.partner.changeset,create_date:0 |
|||
#: field:res.partner.changeset.change,create_date:0 |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,date:0 |
|||
msgid "Date" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,display_name:0 |
|||
#: field:res.partner.changeset,display_name:0 |
|||
#: field:res.partner.changeset.change,display_name:0 |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
msgid "Done" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,field_id:0 |
|||
#: field:res.partner.changeset.change,field_id:0 |
|||
msgid "Field" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset_field_rule |
|||
msgid "Field Rules" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,field_type:0 |
|||
msgid "Field Type" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
msgid "Group By" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,id:0 field:res.partner.changeset,id:0 |
|||
#: field:res.partner.changeset.change,id:0 |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,source_model_id:0 |
|||
msgid "" |
|||
"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.\n" |
|||
"Rules 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." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,__last_update:0 |
|||
#: field:res.partner.changeset,__last_update:0 |
|||
#: field:res.partner.changeset.change,__last_update:0 |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_uid:0 |
|||
#: field:res.partner.changeset,write_uid:0 |
|||
#: field:res.partner.changeset.change,write_uid:0 |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_date:0 |
|||
#: field:res.partner.changeset,write_date:0 |
|||
#: field:res.partner.changeset.change,write_date:0 |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,new_value_boolean:0 |
|||
#: field:res.partner.changeset.change,new_value_char:0 |
|||
#: field:res.partner.changeset.change,new_value_date:0 |
|||
#: field:res.partner.changeset.change,new_value_datetime:0 |
|||
#: field:res.partner.changeset.change,new_value_display:0 |
|||
#: field:res.partner.changeset.change,new_value_float:0 |
|||
#: field:res.partner.changeset.change,new_value_integer:0 |
|||
#: field:res.partner.changeset.change,new_value_reference:0 |
|||
#: field:res.partner.changeset.change,new_value_text:0 |
|||
msgid "New" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,note:0 |
|||
msgid "Note" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,old_value_boolean:0 |
|||
#: field:res.partner.changeset.change,old_value_char:0 |
|||
#: field:res.partner.changeset.change,old_value_date:0 |
|||
#: field:res.partner.changeset.change,old_value_datetime:0 |
|||
#: field:res.partner.changeset.change,old_value_float:0 |
|||
#: field:res.partner.changeset.change,old_value_integer:0 |
|||
#: field:res.partner.changeset.change,old_value_reference:0 |
|||
#: field:res.partner.changeset.change,old_value_text:0 |
|||
msgid "Old" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,partner_id:0 |
|||
msgid "Partner" |
|||
msgstr "Empresa" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_res_partner_changeset_view |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_tree |
|||
msgid "Partner Changeset" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset_change |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Partner Changeset Change" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset |
|||
msgid "Partner Changesets" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Pending" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner:partner_changeset.view_res_partner_filter |
|||
msgid "Pending Changesets" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: field:res.partner.changeset.change,origin_value_boolean:0 |
|||
#: field:res.partner.changeset.change,origin_value_char:0 |
|||
#: field:res.partner.changeset.change,origin_value_date:0 |
|||
#: field:res.partner.changeset.change,origin_value_datetime:0 |
|||
#: field:res.partner.changeset.change,origin_value_display:0 |
|||
#: field:res.partner.changeset.change,origin_value_float:0 |
|||
#: field:res.partner.changeset.change,origin_value_integer:0 |
|||
#: field:res.partner.changeset.change,origin_value_reference:0 |
|||
#: field:res.partner.changeset.change,origin_value_text:0 |
|||
msgid "Previous" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Reject" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Reject pending changes" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Rejected" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,source_model_id:0 |
|||
msgid "Source Model" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,source:0 |
|||
msgid "Source of the change" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,state:0 |
|||
#: field:res.partner.changeset.change,state:0 |
|||
msgid "State" |
|||
msgstr "Estado" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_user |
|||
msgid "The user will be able to apply or reject changesets." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_manager |
|||
msgid "" |
|||
"The user will have an access to the configuration of the changeset rules." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:401 |
|||
#, python-format |
|||
msgid "" |
|||
"This change cannot be applied because a previous changeset for the same partner is pending.\n" |
|||
"Apply all the anterior changesets before applying this one." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:416 |
|||
#, python-format |
|||
msgid "This change has already be applied." |
|||
msgstr "" |
@ -0,0 +1,333 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * partner_changeset |
|||
# |
|||
# Translators: |
|||
# OCA Transbot <transbot@odoo-community.org>, 2016 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2016-07-03 00:45+0000\n" |
|||
"PO-Revision-Date: 2016-07-03 00:45+0000\n" |
|||
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2016\n" |
|||
"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Language: fr\n" |
|||
"Plural-Forms: nplurals=2; plural=(n > 1);\n" |
|||
|
|||
#. module: partner_changeset |
|||
#: sql_constraint:changeset.field.rule:0 |
|||
msgid "A rule already exists for this field." |
|||
msgstr "Une règle existe déjà pour ce champ." |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Accepted" |
|||
msgstr "Accepté" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,action:0 |
|||
msgid "Action" |
|||
msgstr "Action" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Apply" |
|||
msgstr "Appliquer" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Apply pending changes" |
|||
msgstr "Appliquer les modifications en attente" |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,action:0 |
|||
msgid "" |
|||
"Auto: always apply a change.\n" |
|||
"Validate: manually applied by an administrator.\n" |
|||
"Never: change never applied." |
|||
msgstr "" |
|||
"Auto: toujours appliquer une modification.\n" |
|||
"Validation: modification manuellement appliquée par un administrateur.\n" |
|||
"Jamais: modification jamais appliquée." |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner:partner_changeset.res_partner_view_buttons |
|||
#: field:res.partner,count_pending_changesets:0 |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: field:res.partner.changeset,change_ids:0 |
|||
msgid "Changes" |
|||
msgstr "Modifications" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,changeset_id:0 |
|||
msgid "Changeset" |
|||
msgstr "Jeu de modifications" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_manager |
|||
msgid "Changeset Configuration" |
|||
msgstr "Configuration des modifications" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_changeset_field_rule |
|||
msgid "Changeset Field Rules" |
|||
msgstr "Règles de modifications par champ" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_form |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_search |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_tree |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_changeset_field_rule_view |
|||
msgid "Changeset Fields Rules" |
|||
msgstr "Règles de modifications par champ" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_res_partner_changeset |
|||
#: field:res.partner,changeset_ids:0 |
|||
msgid "Changesets" |
|||
msgstr "Modifications" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_user |
|||
msgid "Changesets Validations " |
|||
msgstr "Validations des modifications" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_uid:0 |
|||
#: field:res.partner.changeset,create_uid:0 |
|||
#: field:res.partner.changeset.change,create_uid:0 |
|||
msgid "Created by" |
|||
msgstr "Créé par" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_date:0 |
|||
#: field:res.partner.changeset,create_date:0 |
|||
#: field:res.partner.changeset.change,create_date:0 |
|||
msgid "Created on" |
|||
msgstr "Créé le" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,date:0 |
|||
msgid "Date" |
|||
msgstr "Date" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,display_name:0 |
|||
#: field:res.partner.changeset,display_name:0 |
|||
#: field:res.partner.changeset.change,display_name:0 |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
msgid "Done" |
|||
msgstr "Fait" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,field_id:0 |
|||
#: field:res.partner.changeset.change,field_id:0 |
|||
msgid "Field" |
|||
msgstr "Champ" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset_field_rule |
|||
msgid "Field Rules" |
|||
msgstr "Règle par champ" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,field_type:0 |
|||
msgid "Field Type" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
msgid "Group By" |
|||
msgstr "Grouper par" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,id:0 field:res.partner.changeset,id:0 |
|||
#: field:res.partner.changeset.change,id:0 |
|||
msgid "ID" |
|||
msgstr "Id." |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,source_model_id:0 |
|||
msgid "" |
|||
"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.\n" |
|||
"Rules 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." |
|||
msgstr "" |
|||
"Si un modèle source est défini, la règle ne sera appliquée que lorsque le changement vient de cette source. Les règles sans modèle source sont globales et sont appliquées à toutes les sources.\n" |
|||
" Les règles avec une souce ont la précédence sur les règles globales. Si un champ n'a pas de règle avec source, la règle globale est utilisée." |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,__last_update:0 |
|||
#: field:res.partner.changeset,__last_update:0 |
|||
#: field:res.partner.changeset.change,__last_update:0 |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_uid:0 |
|||
#: field:res.partner.changeset,write_uid:0 |
|||
#: field:res.partner.changeset.change,write_uid:0 |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_date:0 |
|||
#: field:res.partner.changeset,write_date:0 |
|||
#: field:res.partner.changeset.change,write_date:0 |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,new_value_boolean:0 |
|||
#: field:res.partner.changeset.change,new_value_char:0 |
|||
#: field:res.partner.changeset.change,new_value_date:0 |
|||
#: field:res.partner.changeset.change,new_value_datetime:0 |
|||
#: field:res.partner.changeset.change,new_value_display:0 |
|||
#: field:res.partner.changeset.change,new_value_float:0 |
|||
#: field:res.partner.changeset.change,new_value_integer:0 |
|||
#: field:res.partner.changeset.change,new_value_reference:0 |
|||
#: field:res.partner.changeset.change,new_value_text:0 |
|||
msgid "New" |
|||
msgstr "Nouveau" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,note:0 |
|||
msgid "Note" |
|||
msgstr "Note" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,old_value_boolean:0 |
|||
#: field:res.partner.changeset.change,old_value_char:0 |
|||
#: field:res.partner.changeset.change,old_value_date:0 |
|||
#: field:res.partner.changeset.change,old_value_datetime:0 |
|||
#: field:res.partner.changeset.change,old_value_float:0 |
|||
#: field:res.partner.changeset.change,old_value_integer:0 |
|||
#: field:res.partner.changeset.change,old_value_reference:0 |
|||
#: field:res.partner.changeset.change,old_value_text:0 |
|||
msgid "Old" |
|||
msgstr "Ancien" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,partner_id:0 |
|||
msgid "Partner" |
|||
msgstr "Partenaire" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_res_partner_changeset_view |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_tree |
|||
msgid "Partner Changeset" |
|||
msgstr "Modifications de partenaire" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset_change |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Partner Changeset Change" |
|||
msgstr "Modification de partenaire" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset |
|||
msgid "Partner Changesets" |
|||
msgstr "Modifications de partenaire" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Pending" |
|||
msgstr "En attente" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner:partner_changeset.view_res_partner_filter |
|||
msgid "Pending Changesets" |
|||
msgstr "Modifications en attente" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: field:res.partner.changeset.change,origin_value_boolean:0 |
|||
#: field:res.partner.changeset.change,origin_value_char:0 |
|||
#: field:res.partner.changeset.change,origin_value_date:0 |
|||
#: field:res.partner.changeset.change,origin_value_datetime:0 |
|||
#: field:res.partner.changeset.change,origin_value_display:0 |
|||
#: field:res.partner.changeset.change,origin_value_float:0 |
|||
#: field:res.partner.changeset.change,origin_value_integer:0 |
|||
#: field:res.partner.changeset.change,origin_value_reference:0 |
|||
#: field:res.partner.changeset.change,origin_value_text:0 |
|||
msgid "Previous" |
|||
msgstr "Précédent" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Reject" |
|||
msgstr "Rejeter" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Reject pending changes" |
|||
msgstr "Rejeter les modifications en attente" |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Rejected" |
|||
msgstr "Rejeté" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,source_model_id:0 |
|||
msgid "Source Model" |
|||
msgstr "Modèle source" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,source:0 |
|||
msgid "Source of the change" |
|||
msgstr "Source du changement" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,state:0 |
|||
#: field:res.partner.changeset.change,state:0 |
|||
msgid "State" |
|||
msgstr "État" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_user |
|||
msgid "The user will be able to apply or reject changesets." |
|||
msgstr "L'utilisateur pourra appliquer ou rejeter les jeux de modifications." |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_manager |
|||
msgid "" |
|||
"The user will have an access to the configuration of the changeset rules." |
|||
msgstr "" |
|||
"L'utilisateur aura accès à la configuration des règles de jeux de " |
|||
"modifications." |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:401 |
|||
#, python-format |
|||
msgid "" |
|||
"This change cannot be applied because a previous changeset for the same partner is pending.\n" |
|||
"Apply all the anterior changesets before applying this one." |
|||
msgstr "" |
|||
"The changement ne peux pas être appliqué car un précédent jeu de modification pour le même partenaire est toujours en attente.\n" |
|||
"Il est nécessaire d'appliquer tous les précédents jeux de modification avant d'applique celui-ci." |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:416 |
|||
#, python-format |
|||
msgid "This change has already be applied." |
|||
msgstr "The changement est déjà appliqué." |
@ -0,0 +1,289 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * partner_changeset |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-11-25 08:31+0000\n" |
|||
"PO-Revision-Date: 2015-11-25 08:31+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: partner_changeset |
|||
#: sql_constraint:changeset.field.rule:0 |
|||
msgid "A rule already exists for this field." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Accepted" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,action:0 |
|||
msgid "Action" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Apply" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Apply pending changes" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,action:0 |
|||
msgid "Auto: always apply a change.\n" |
|||
"Validate: manually applied by an administrator.\n" |
|||
"Never: change never applied." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner:partner_changeset.res_partner_view_buttons |
|||
#: field:res.partner.changeset,change_ids:0 |
|||
msgid "Changes" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,changeset_id:0 |
|||
msgid "Changeset" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_manager |
|||
msgid "Changeset Configuration" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_changeset_field_rule |
|||
msgid "Changeset Field Rules" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_form |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_search |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_tree |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_changeset_field_rule_view |
|||
msgid "Changeset Fields Rules" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_res_partner_changeset |
|||
#: field:res.partner,changeset_ids:0 |
|||
msgid "Changesets" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_user |
|||
msgid "Changesets Validations " |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_uid:0 |
|||
#: field:res.partner.changeset,create_uid:0 |
|||
#: field:res.partner.changeset.change,create_uid:0 |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_date:0 |
|||
#: field:res.partner.changeset,create_date:0 |
|||
#: field:res.partner.changeset.change,create_date:0 |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,date:0 |
|||
msgid "Date" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
msgid "Done" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,field_id:0 |
|||
#: field:res.partner.changeset.change,field_id:0 |
|||
msgid "Field" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset_field_rule |
|||
msgid "Field Rules" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
msgid "Group By" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,id:0 |
|||
#: field:res.partner.changeset,id:0 |
|||
#: field:res.partner.changeset.change,id:0 |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,source_model_id:0 |
|||
msgid "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.\n" |
|||
"Rules 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." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_uid:0 |
|||
#: field:res.partner.changeset,write_uid:0 |
|||
#: field:res.partner.changeset.change,write_uid:0 |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_date:0 |
|||
#: field:res.partner.changeset,write_date:0 |
|||
#: field:res.partner.changeset.change,write_date:0 |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,new_value_boolean:0 |
|||
#: field:res.partner.changeset.change,new_value_char:0 |
|||
#: field:res.partner.changeset.change,new_value_date:0 |
|||
#: field:res.partner.changeset.change,new_value_datetime:0 |
|||
#: field:res.partner.changeset.change,new_value_float:0 |
|||
#: field:res.partner.changeset.change,new_value_integer:0 |
|||
#: field:res.partner.changeset.change,new_value_reference:0 |
|||
#: field:res.partner.changeset.change,new_value_text:0 |
|||
msgid "New" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,note:0 |
|||
msgid "Note" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,old_value_boolean:0 |
|||
#: field:res.partner.changeset.change,old_value_char:0 |
|||
#: field:res.partner.changeset.change,old_value_date:0 |
|||
#: field:res.partner.changeset.change,old_value_datetime:0 |
|||
#: field:res.partner.changeset.change,old_value_float:0 |
|||
#: field:res.partner.changeset.change,old_value_integer:0 |
|||
#: field:res.partner.changeset.change,old_value_reference:0 |
|||
#: field:res.partner.changeset.change,old_value_text:0 |
|||
msgid "Old" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,partner_id:0 |
|||
msgid "Partner" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_res_partner_changeset_view |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_tree |
|||
msgid "Partner Changeset" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset_change |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Partner Changeset Change" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset |
|||
msgid "Partner Changesets" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Pending" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner:partner_changeset.view_res_partner_filter |
|||
msgid "Pending Changesets" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Previous" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Reject" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Reject pending changes" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Rejected" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,source_model_id:0 |
|||
msgid "Source Model" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,source:0 |
|||
msgid "Source of the change" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,state:0 |
|||
#: field:res.partner.changeset.change,state:0 |
|||
msgid "State" |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_user |
|||
msgid "The user will be able to apply or reject changesets." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_manager |
|||
msgid "The user will have an access to the configuration of the changeset rules." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:418 |
|||
#, python-format |
|||
msgid "This change cannot be applied because a previous changeset for the same partner is pending.\n" |
|||
"Apply all the anterior changesets before applying this one." |
|||
msgstr "" |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:433 |
|||
#, python-format |
|||
msgid "This change has already be applied." |
|||
msgstr "" |
|||
|
@ -0,0 +1,331 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * partner_changeset |
|||
# |
|||
# Translators: |
|||
# Matjaž Mozetič <m.mozetic@matmoz.si>, 2016 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2016-07-03 07:56+0000\n" |
|||
"PO-Revision-Date: 2016-07-03 07:56+0000\n" |
|||
"Last-Translator: Matjaž Mozetič <m.mozetic@matmoz.si>, 2016\n" |
|||
"Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Language: sl\n" |
|||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" |
|||
|
|||
#. module: partner_changeset |
|||
#: sql_constraint:changeset.field.rule:0 |
|||
msgid "A rule already exists for this field." |
|||
msgstr "Za to polje že obstaja pravilo." |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Accepted" |
|||
msgstr "Sprejeto" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,action:0 |
|||
msgid "Action" |
|||
msgstr "Ukrep" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Apply" |
|||
msgstr "Uveljavi" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Apply pending changes" |
|||
msgstr "Uveljavi čakajoče spremembe" |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,action:0 |
|||
msgid "" |
|||
"Auto: always apply a change.\n" |
|||
"Validate: manually applied by an administrator.\n" |
|||
"Never: change never applied." |
|||
msgstr "" |
|||
"Samodejno: vedno uveljavi spremembo.\n" |
|||
"Overi: ročno uveljavi administrator.\n" |
|||
"Nikoli: sprememba ni nikoli uveljavljena." |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner:partner_changeset.res_partner_view_buttons |
|||
#: field:res.partner,count_pending_changesets:0 |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: field:res.partner.changeset,change_ids:0 |
|||
msgid "Changes" |
|||
msgstr "Spremembe" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,changeset_id:0 |
|||
msgid "Changeset" |
|||
msgstr "Niz sprememb" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_manager |
|||
msgid "Changeset Configuration" |
|||
msgstr "Nastavitve niza sprememb" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_changeset_field_rule |
|||
msgid "Changeset Field Rules" |
|||
msgstr "Pravila polja niza sprememb" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_form |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_search |
|||
#: view:changeset.field.rule:partner_changeset.view_changeset_field_rule_tree |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_changeset_field_rule_view |
|||
msgid "Changeset Fields Rules" |
|||
msgstr "Pravila polj niza sprememb" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_res_partner_changeset |
|||
#: field:res.partner,changeset_ids:0 |
|||
msgid "Changesets" |
|||
msgstr "Nizi sprememb" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,name:partner_changeset.group_changeset_user |
|||
msgid "Changesets Validations " |
|||
msgstr "Overitve nizov sprememb" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_uid:0 |
|||
#: field:res.partner.changeset,create_uid:0 |
|||
#: field:res.partner.changeset.change,create_uid:0 |
|||
msgid "Created by" |
|||
msgstr "Ustvaril" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,create_date:0 |
|||
#: field:res.partner.changeset,create_date:0 |
|||
#: field:res.partner.changeset.change,create_date:0 |
|||
msgid "Created on" |
|||
msgstr "Ustvarjeno" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,date:0 |
|||
msgid "Date" |
|||
msgstr "Datum" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,display_name:0 |
|||
#: field:res.partner.changeset,display_name:0 |
|||
#: field:res.partner.changeset.change,display_name:0 |
|||
msgid "Display Name" |
|||
msgstr "Prikazni naziv" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
msgid "Done" |
|||
msgstr "Opravljeno" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,field_id:0 |
|||
#: field:res.partner.changeset.change,field_id:0 |
|||
msgid "Field" |
|||
msgstr "Polje" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset_field_rule |
|||
msgid "Field Rules" |
|||
msgstr "Pravila polja" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,field_type:0 |
|||
msgid "Field Type" |
|||
msgstr "Tip polja" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
msgid "Group By" |
|||
msgstr "Združi po" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,id:0 field:res.partner.changeset,id:0 |
|||
#: field:res.partner.changeset.change,id:0 |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: partner_changeset |
|||
#: help:changeset.field.rule,source_model_id:0 |
|||
msgid "" |
|||
"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.\n" |
|||
"Rules 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." |
|||
msgstr "" |
|||
"Če je izvorni model določen, se pravilo uveljavi le za spremembe iz tega izvora. Pravila brez izvornega modela so globalna in se uveljavijo na vseh ozadjih.\n" |
|||
"Pravila izvornega modela imajo prednost pred globalnimi pravili, a če polje nima pravila z izvornim modelom, se uporabi globalno pravilo." |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,__last_update:0 |
|||
#: field:res.partner.changeset,__last_update:0 |
|||
#: field:res.partner.changeset.change,__last_update:0 |
|||
msgid "Last Modified on" |
|||
msgstr "Zadnjič spremenjeno" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_uid:0 |
|||
#: field:res.partner.changeset,write_uid:0 |
|||
#: field:res.partner.changeset.change,write_uid:0 |
|||
msgid "Last Updated by" |
|||
msgstr "Zadnji posodobil" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,write_date:0 |
|||
#: field:res.partner.changeset,write_date:0 |
|||
#: field:res.partner.changeset.change,write_date:0 |
|||
msgid "Last Updated on" |
|||
msgstr "Zadnjič posodobljeno" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,new_value_boolean:0 |
|||
#: field:res.partner.changeset.change,new_value_char:0 |
|||
#: field:res.partner.changeset.change,new_value_date:0 |
|||
#: field:res.partner.changeset.change,new_value_datetime:0 |
|||
#: field:res.partner.changeset.change,new_value_display:0 |
|||
#: field:res.partner.changeset.change,new_value_float:0 |
|||
#: field:res.partner.changeset.change,new_value_integer:0 |
|||
#: field:res.partner.changeset.change,new_value_reference:0 |
|||
#: field:res.partner.changeset.change,new_value_text:0 |
|||
msgid "New" |
|||
msgstr "Novo" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,note:0 |
|||
msgid "Note" |
|||
msgstr "Opomba" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset.change,old_value_boolean:0 |
|||
#: field:res.partner.changeset.change,old_value_char:0 |
|||
#: field:res.partner.changeset.change,old_value_date:0 |
|||
#: field:res.partner.changeset.change,old_value_datetime:0 |
|||
#: field:res.partner.changeset.change,old_value_float:0 |
|||
#: field:res.partner.changeset.change,old_value_integer:0 |
|||
#: field:res.partner.changeset.change,old_value_reference:0 |
|||
#: field:res.partner.changeset.change,old_value_text:0 |
|||
msgid "Old" |
|||
msgstr "Staro" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,partner_id:0 |
|||
msgid "Partner" |
|||
msgstr "Partner" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.actions.act_window,name:partner_changeset.action_res_partner_changeset_view |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_tree |
|||
msgid "Partner Changeset" |
|||
msgstr "Niz sprememb partnerja" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.model,name:partner_changeset.model_res_partner_changeset_change |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Partner Changeset Change" |
|||
msgstr "Sprememba niza sprememb partnerja" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:ir.ui.menu,name:partner_changeset.menu_changeset |
|||
msgid "Partner Changesets" |
|||
msgstr "Nizi sprememb partnerja" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: selection:res.partner.changeset,state:0 |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Pending" |
|||
msgstr "Čakanje" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner:partner_changeset.view_res_partner_filter |
|||
msgid "Pending Changesets" |
|||
msgstr "Čakajoči nizi sprememb" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: field:res.partner.changeset.change,origin_value_boolean:0 |
|||
#: field:res.partner.changeset.change,origin_value_char:0 |
|||
#: field:res.partner.changeset.change,origin_value_date:0 |
|||
#: field:res.partner.changeset.change,origin_value_datetime:0 |
|||
#: field:res.partner.changeset.change,origin_value_display:0 |
|||
#: field:res.partner.changeset.change,origin_value_float:0 |
|||
#: field:res.partner.changeset.change,origin_value_integer:0 |
|||
#: field:res.partner.changeset.change,origin_value_reference:0 |
|||
#: field:res.partner.changeset.change,origin_value_text:0 |
|||
msgid "Previous" |
|||
msgstr "Prejšnji" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
#: view:res.partner.changeset.change:partner_changeset.view_res_partner_changeset_change_form |
|||
msgid "Reject" |
|||
msgstr "Zavrni" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_form |
|||
msgid "Reject pending changes" |
|||
msgstr "Zavrni čakajoče spremembe" |
|||
|
|||
#. module: partner_changeset |
|||
#: selection:res.partner.changeset.change,state:0 |
|||
msgid "Rejected" |
|||
msgstr "Zavrnjeno" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:changeset.field.rule,source_model_id:0 |
|||
msgid "Source Model" |
|||
msgstr "Izvorni model" |
|||
|
|||
#. module: partner_changeset |
|||
#: field:res.partner.changeset,source:0 |
|||
msgid "Source of the change" |
|||
msgstr "Izvor spremembe" |
|||
|
|||
#. module: partner_changeset |
|||
#: view:res.partner.changeset:partner_changeset.view_res_partner_changeset_search |
|||
#: field:res.partner.changeset,state:0 |
|||
#: field:res.partner.changeset.change,state:0 |
|||
msgid "State" |
|||
msgstr "Stanje" |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_user |
|||
msgid "The user will be able to apply or reject changesets." |
|||
msgstr "Uporabnik bo lahko uveljavljal ali zavračal nize sprememb." |
|||
|
|||
#. module: partner_changeset |
|||
#: model:res.groups,comment:partner_changeset.group_changeset_manager |
|||
msgid "" |
|||
"The user will have an access to the configuration of the changeset rules." |
|||
msgstr "Uporabnik bo imel dostop do nastavitev pravil niza sprememb." |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:401 |
|||
#, python-format |
|||
msgid "" |
|||
"This change cannot be applied because a previous changeset for the same partner is pending.\n" |
|||
"Apply all the anterior changesets before applying this one." |
|||
msgstr "" |
|||
"Te spremembe ni mogoče uveljaviti, ker obstaja za istega partnerja prejšnji čakajoč niz sprememb.\n" |
|||
"Pred uveljavitvijo tega uveljavite vse predhodne nize sprememb." |
|||
|
|||
#. module: partner_changeset |
|||
#: code:addons/partner_changeset/models/res_partner_changeset.py:416 |
|||
#, python-format |
|||
msgid "This change has already be applied." |
|||
msgstr "Ta sprememba je že uveljavljena." |
@ -0,0 +1,7 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from . import res_partner |
|||
from . import res_partner_changeset |
|||
from . import changeset_field_rule |
@ -0,0 +1,144 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from openerp import models, fields, api |
|||
from openerp.tools.cache import ormcache |
|||
|
|||
|
|||
class ChangesetFieldRule(models.Model): |
|||
_name = 'changeset.field.rule' |
|||
_description = 'Changeset Field Rules' |
|||
_rec_name = 'field_id' |
|||
|
|||
field_id = fields.Many2one( |
|||
comodel_name='ir.model.fields', |
|||
string='Field', |
|||
domain="[('model_id.model', '=', 'res.partner'), " |
|||
" ('ttype', 'in', ('char', 'selection', 'date', 'datetime', " |
|||
" 'float', 'integer', 'text', 'boolean', " |
|||
" 'many2one')), " |
|||
" ('readonly', '=', False)]", |
|||
ondelete='cascade', |
|||
required=True, |
|||
) |
|||
action = fields.Selection( |
|||
selection='_selection_action', |
|||
string='Action', |
|||
required=True, |
|||
help="Auto: always apply a change.\n" |
|||
"Validate: manually applied by an administrator.\n" |
|||
"Never: change never applied.", |
|||
) |
|||
source_model_id = fields.Many2one( |
|||
comodel_name='ir.model', |
|||
string='Source Model', |
|||
ondelete='cascade', |
|||
domain=lambda self: [('id', 'in', self._domain_source_models().ids)], |
|||
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.\n" |
|||
"Rules 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." |
|||
) |
|||
|
|||
_sql_constraints = [ |
|||
('model_field_uniq', |
|||
'unique (source_model_id, field_id)', |
|||
'A rule already exists for this field.'), |
|||
] |
|||
|
|||
@api.model |
|||
def _domain_source_models(self): |
|||
""" Returns the models for which we can define rules. |
|||
|
|||
Example for submodules (replace by the xmlid of the model): |
|||
|
|||
:: |
|||
models = super(ChangesetFieldRule, self)._domain_source_models() |
|||
return models | self.env.ref('base.model_res_users') |
|||
|
|||
Rules without model are global and apply for all models. |
|||
|
|||
""" |
|||
return self.env.ref('base.model_res_users') |
|||
|
|||
@api.model |
|||
def _selection_action(self): |
|||
return [('auto', 'Auto'), |
|||
('validate', 'Validate'), |
|||
('never', 'Never'), |
|||
] |
|||
|
|||
@ormcache(skiparg=1) |
|||
@api.model |
|||
def _get_rules(self, source_model_name): |
|||
""" Cache rules |
|||
|
|||
Keep only the id of the rules, because if we keep the recordsets |
|||
in the ormcache, we won't be able to browse them once their |
|||
cursor is closed. |
|||
|
|||
The public method ``get_rules`` return the rules with the recordsets |
|||
when called. |
|||
|
|||
""" |
|||
model_rules = self.search( |
|||
['|', ('source_model_id.model', '=', source_model_name), |
|||
('source_model_id', '=', False)], |
|||
# using 'ASC' means that 'NULLS LAST' is the default |
|||
order='source_model_id ASC', |
|||
) |
|||
# model's rules have precedence over global ones so we iterate |
|||
# over rules which have a source model first, then we complete |
|||
# them with the global rules |
|||
result = {} |
|||
for rule in model_rules: |
|||
# we already have a model's rule |
|||
if result.get(rule.field_id.name): |
|||
continue |
|||
result[rule.field_id.name] = rule.id |
|||
return result |
|||
|
|||
@api.model |
|||
def get_rules(self, source_model_name): |
|||
""" 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. |
|||
""" |
|||
rules = {} |
|||
cached_rules = self._get_rules(source_model_name) |
|||
for field, rule_id in cached_rules.iteritems(): |
|||
rules[field] = self.browse(rule_id) |
|||
return rules |
|||
|
|||
@api.model |
|||
def create(self, vals): |
|||
record = super(ChangesetFieldRule, self).create(vals) |
|||
self.clear_caches() |
|||
return record |
|||
|
|||
@api.multi |
|||
def write(self, vals): |
|||
result = super(ChangesetFieldRule, self).write(vals) |
|||
self.clear_caches() |
|||
return result |
|||
|
|||
@api.multi |
|||
def unlink(self): |
|||
result = super(ChangesetFieldRule, self).unlink() |
|||
self.clear_caches() |
|||
return result |
@ -0,0 +1,50 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from openerp import models, fields, api |
|||
|
|||
|
|||
class ResPartner(models.Model): |
|||
_inherit = 'res.partner' |
|||
|
|||
changeset_ids = fields.One2many(comodel_name='res.partner.changeset', |
|||
inverse_name='partner_id', |
|||
string='Changesets', |
|||
readonly=True) |
|||
count_pending_changesets = fields.Integer( |
|||
string='Changes', |
|||
compute='_count_pending_changesets', |
|||
search='_search_count_pending_changesets') |
|||
|
|||
@api.one |
|||
@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 |
|||
) |
|||
self.count_pending_changesets = len(changesets) |
|||
|
|||
@api.multi |
|||
def write(self, values): |
|||
if self.env.context.get('__no_changeset'): |
|||
return super(ResPartner, self).write(values) |
|||
else: |
|||
changeset_model = self.env['res.partner.changeset'] |
|||
for record in self: |
|||
local_values = changeset_model.add_changeset(record, values) |
|||
super(ResPartner, record).write(local_values) |
|||
return True |
|||
|
|||
def _search_count_pending_changesets(self, operator, value): |
|||
if operator not in ('=', '!=', '<', '<=', '>', '>=', 'in', 'not in'): |
|||
return [] |
|||
query = ("SELECT p.id " |
|||
"FROM res_partner p " |
|||
"INNER JOIN res_partner_changeset r ON r.partner_id = p.id " |
|||
"WHERE r.state = 'draft' " |
|||
"GROUP BY p.id " |
|||
"HAVING COUNT(r.id) %s %%s ") % operator |
|||
self.env.cr.execute(query, (value,)) |
|||
ids = [row[0] for row in self.env.cr.fetchall()] |
|||
return [('id', 'in', ids)] |
@ -0,0 +1,514 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from itertools import groupby |
|||
from lxml import etree |
|||
from operator import attrgetter |
|||
|
|||
from openerp import models, fields, api, exceptions, _ |
|||
from openerp.osv.orm import setup_modifiers |
|||
|
|||
# sentinel object to be sure that no empty value was passed to |
|||
# ResPartnerChangesetChange._value_for_changeset |
|||
_NO_VALUE = object() |
|||
|
|||
|
|||
class ResPartnerChangeset(models.Model): |
|||
_name = 'res.partner.changeset' |
|||
_description = 'Partner Changeset' |
|||
_order = 'date desc' |
|||
_rec_name = 'date' |
|||
|
|||
partner_id = fields.Many2one(comodel_name='res.partner', |
|||
string='Partner', |
|||
select=True, |
|||
required=True, |
|||
readonly=True, |
|||
ondelete='cascade') |
|||
change_ids = fields.One2many(comodel_name='res.partner.changeset.change', |
|||
inverse_name='changeset_id', |
|||
string='Changes', |
|||
readonly=True) |
|||
date = fields.Datetime(default=fields.Datetime.now, |
|||
select=True, |
|||
readonly=True) |
|||
state = fields.Selection( |
|||
compute='_compute_state', |
|||
selection=[('draft', 'Pending'), |
|||
('done', 'Done')], |
|||
string='State', |
|||
store=True, |
|||
) |
|||
note = fields.Text() |
|||
source = fields.Reference( |
|||
string='Source of the change', |
|||
selection='_reference_models', |
|||
readonly=True, |
|||
) |
|||
|
|||
@api.model |
|||
def _reference_models(self): |
|||
models = self.env['ir.model'].search([]) |
|||
return [(model.model, model.name) for model in models] |
|||
|
|||
@api.one |
|||
@api.depends('change_ids', 'change_ids.state') |
|||
def _compute_state(self): |
|||
if all(change.state in ('done', 'cancel') for change |
|||
in self.mapped('change_ids')): |
|||
self.state = 'done' |
|||
else: |
|||
self.state = 'draft' |
|||
|
|||
@api.multi |
|||
def apply(self): |
|||
self.mapped('change_ids').apply() |
|||
|
|||
@api.multi |
|||
def cancel(self): |
|||
self.mapped('change_ids').cancel() |
|||
|
|||
@api.multi |
|||
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 |
|||
system, the the changeset will follow the rules configured for |
|||
the 'Users' / 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 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() |
|||
|
|||
source_model = self.env.context.get('__changeset_rules_source_model') |
|||
source_id = self.env.context.get('__changeset_rules_source_id') |
|||
if not source_model: |
|||
# if the changes source is not defined, log the user who |
|||
# made the change |
|||
source_model = 'res.users' |
|||
if not source_id: |
|||
source_id = self.env.uid |
|||
if source_model and source_id: |
|||
source = '%s,%s' % (source_model, source_id) |
|||
else: |
|||
source = False |
|||
|
|||
change_model = self.env['res.partner.changeset.change'] |
|||
write_values = values.copy() |
|||
changes = [] |
|||
rules = self.env['changeset.field.rule'].get_rules( |
|||
source_model_name=source_model, |
|||
) |
|||
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_changeset_change( |
|||
record, rule, field, values[field] |
|||
) |
|||
if pop_value: |
|||
write_values.pop(field) |
|||
changes.append(change) |
|||
if changes: |
|||
self.env['res.partner.changeset'].create({ |
|||
'partner_id': record.id, |
|||
'change_ids': [(0, 0, vals) for vals in changes], |
|||
'date': fields.Datetime.now(), |
|||
'source': source, |
|||
}) |
|||
return write_values |
|||
|
|||
|
|||
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: |
|||
|
|||
* 'origin' |
|||
* 'old' |
|||
* 'new' |
|||
|
|||
The 'new' fields contain the value that needs to be validated. |
|||
The 'old' field copies the actual value of the partner when the |
|||
change is either applied either canceled. This field is used as a storage |
|||
place but never shown by itself. |
|||
The 'origin' fields is a related field towards the actual values of |
|||
the partner until the change is either applied either canceled, past |
|||
that it shows the 'old' value. |
|||
The reason behind this is that the values may change on a partner between |
|||
the moment when the changeset is created and when it is applied. |
|||
|
|||
On the views, we show the origin fields which represent the actual |
|||
partner values or the old values and we show the new fields. |
|||
|
|||
The 'origin' and 'new_value_display' are displayed on |
|||
the tree view where we need a unique of field, the other fields are |
|||
displayed on the form view so we benefit from their widgets. |
|||
|
|||
""" |
|||
_name = 'res.partner.changeset.change' |
|||
_description = 'Partner Changeset Change' |
|||
_rec_name = 'field_id' |
|||
|
|||
changeset_id = fields.Many2one(comodel_name='res.partner.changeset', |
|||
required=True, |
|||
string='Changeset', |
|||
ondelete='cascade', |
|||
readonly=True) |
|||
field_id = fields.Many2one(comodel_name='ir.model.fields', |
|||
string='Field', |
|||
required=True, |
|||
readonly=True) |
|||
field_type = fields.Selection(related='field_id.ttype', |
|||
string='Field Type', |
|||
readonly=True) |
|||
|
|||
origin_value_display = fields.Char( |
|||
string='Previous', |
|||
compute='_compute_value_display', |
|||
) |
|||
new_value_display = fields.Char( |
|||
string='New', |
|||
compute='_compute_value_display', |
|||
) |
|||
|
|||
# Fields showing the origin partner's value or the 'old' value if |
|||
# the change is applied or canceled. |
|||
origin_value_char = fields.Char(compute='_compute_origin_values', |
|||
string='Previous', |
|||
readonly=True) |
|||
origin_value_date = fields.Date(compute='_compute_origin_values', |
|||
string='Previous', |
|||
readonly=True) |
|||
origin_value_datetime = fields.Datetime(compute='_compute_origin_values', |
|||
string='Previous', |
|||
readonly=True) |
|||
origin_value_float = fields.Float(compute='_compute_origin_values', |
|||
string='Previous', |
|||
readonly=True) |
|||
origin_value_integer = fields.Integer(compute='_compute_origin_values', |
|||
string='Previous', |
|||
readonly=True) |
|||
origin_value_text = fields.Text(compute='_compute_origin_values', |
|||
string='Previous', |
|||
readonly=True) |
|||
origin_value_boolean = fields.Boolean(compute='_compute_origin_values', |
|||
string='Previous', |
|||
readonly=True) |
|||
origin_value_reference = fields.Reference( |
|||
compute='_compute_origin_values', |
|||
string='Previous', |
|||
selection='_reference_models', |
|||
readonly=True, |
|||
) |
|||
|
|||
# Fields storing the previous partner's values (saved when the |
|||
# changeset is applied) |
|||
old_value_char = fields.Char(string='Old', |
|||
readonly=True) |
|||
old_value_date = fields.Date(string='Old', |
|||
readonly=True) |
|||
old_value_datetime = fields.Datetime(string='Old', |
|||
readonly=True) |
|||
old_value_float = fields.Float(string='Old', |
|||
readonly=True) |
|||
old_value_integer = fields.Integer(string='Old', |
|||
readonly=True) |
|||
old_value_text = fields.Text(string='Old', |
|||
readonly=True) |
|||
old_value_boolean = fields.Boolean(string='Old', |
|||
readonly=True) |
|||
old_value_reference = fields.Reference(string='Old', |
|||
selection='_reference_models', |
|||
readonly=True) |
|||
|
|||
# Fields storing the value applied on the partner |
|||
new_value_char = fields.Char(string='New', |
|||
readonly=True) |
|||
new_value_date = fields.Date(string='New', |
|||
readonly=True) |
|||
new_value_datetime = fields.Datetime(string='New', |
|||
readonly=True) |
|||
new_value_float = fields.Float(string='New', |
|||
readonly=True) |
|||
new_value_integer = fields.Integer(string='New', |
|||
readonly=True) |
|||
new_value_text = fields.Text(string='New', |
|||
readonly=True) |
|||
new_value_boolean = fields.Boolean(string='New', |
|||
readonly=True) |
|||
new_value_reference = fields.Reference(string='New', |
|||
selection='_reference_models', |
|||
readonly=True) |
|||
|
|||
state = fields.Selection( |
|||
selection=[('draft', 'Pending'), |
|||
('done', 'Accepted'), |
|||
('cancel', 'Rejected'), |
|||
], |
|||
required=True, |
|||
default='draft', |
|||
readonly=True, |
|||
) |
|||
|
|||
@api.model |
|||
def _reference_models(self): |
|||
models = self.env['ir.model'].search([]) |
|||
return [(model.model, model.name) for model in models] |
|||
|
|||
_suffix_to_types = { |
|||
'char': ('char', 'selection'), |
|||
'date': ('date',), |
|||
'datetime': ('datetime',), |
|||
'float': ('float',), |
|||
'integer': ('integer',), |
|||
'text': ('text',), |
|||
'boolean': ('boolean',), |
|||
'reference': ('many2one',), |
|||
} |
|||
|
|||
_type_to_suffix = {ftype: suffix |
|||
for suffix, ftypes in _suffix_to_types.iteritems() |
|||
for ftype in ftypes} |
|||
|
|||
_origin_value_fields = ['origin_value_%s' % suffix |
|||
for suffix in _suffix_to_types] |
|||
_old_value_fields = ['old_value_%s' % suffix |
|||
for suffix in _suffix_to_types] |
|||
_new_value_fields = ['new_value_%s' % suffix |
|||
for suffix in _suffix_to_types] |
|||
_value_fields = (_origin_value_fields + |
|||
_old_value_fields + |
|||
_new_value_fields) |
|||
|
|||
@api.one |
|||
@api.depends('changeset_id.partner_id') |
|||
def _compute_origin_values(self): |
|||
field_name = self.get_field_for_type(self.field_id, 'origin') |
|||
if self.state == 'draft': |
|||
value = self.changeset_id.partner_id[self.field_id.name] |
|||
else: |
|||
old_field = self.get_field_for_type(self.field_id, 'old') |
|||
value = self[old_field] |
|||
setattr(self, field_name, value) |
|||
|
|||
@api.one |
|||
@api.depends(lambda self: self._value_fields) |
|||
def _compute_value_display(self): |
|||
for prefix in ('origin', 'new'): |
|||
value = getattr(self, 'get_%s_value' % prefix)() |
|||
if self.field_id.ttype == 'many2one' and value: |
|||
value = value.display_name |
|||
setattr(self, '%s_value_display' % prefix, value) |
|||
|
|||
@api.model |
|||
def get_field_for_type(self, field, prefix): |
|||
assert prefix in ('origin', 'old', 'new') |
|||
field_type = self._type_to_suffix.get(field.ttype) |
|||
if not field_type: |
|||
raise NotImplementedError( |
|||
'field type %s is not supported' % field_type |
|||
) |
|||
return '%s_value_%s' % (prefix, field_type) |
|||
|
|||
@api.multi |
|||
def get_origin_value(self): |
|||
self.ensure_one() |
|||
field_name = self.get_field_for_type(self.field_id, 'origin') |
|||
return self[field_name] |
|||
|
|||
@api.multi |
|||
def get_new_value(self): |
|||
self.ensure_one() |
|||
field_name = self.get_field_for_type(self.field_id, 'new') |
|||
return self[field_name] |
|||
|
|||
@api.multi |
|||
def set_old_value(self): |
|||
""" Copy the value of the partner to the 'old' field """ |
|||
for change in self: |
|||
# copy the existing partner's value for the history |
|||
old_value_for_write = self._value_for_changeset( |
|||
change.changeset_id.partner_id, |
|||
change.field_id.name |
|||
) |
|||
old_field_name = self.get_field_for_type(change.field_id, 'old') |
|||
change.write({old_field_name: old_value_for_write}) |
|||
|
|||
@api.multi |
|||
def apply(self): |
|||
""" Apply the change on the changeset's partner |
|||
|
|||
It is optimized thus that it makes only one write on the partner |
|||
per changeset if many changes are applied at once. |
|||
""" |
|||
changes_ok = self.browse() |
|||
key = attrgetter('changeset_id') |
|||
for changeset, changes in groupby(self.sorted(key=key), key=key): |
|||
values = {} |
|||
partner = changeset.partner_id |
|||
for change in changes: |
|||
if change.state in ('cancel', 'done'): |
|||
continue |
|||
|
|||
field = change.field_id |
|||
value_for_write = change._convert_value_for_write( |
|||
change.get_new_value() |
|||
) |
|||
values[field.name] = value_for_write |
|||
|
|||
change.set_old_value() |
|||
|
|||
changes_ok |= change |
|||
|
|||
if not values: |
|||
continue |
|||
|
|||
previous_changesets = self.env['res.partner.changeset'].search( |
|||
[('date', '<', changeset.date), |
|||
('state', '=', 'draft'), |
|||
('partner_id', '=', changeset.partner_id.id), |
|||
], |
|||
limit=1, |
|||
) |
|||
if previous_changesets: |
|||
raise exceptions.Warning( |
|||
_('This change cannot be applied because a previous ' |
|||
'changeset for the same partner is pending.\n' |
|||
'Apply all the anterior changesets before applying ' |
|||
'this one.') |
|||
) |
|||
|
|||
partner.with_context(__no_changeset=True).write(values) |
|||
|
|||
changes_ok.write({'state': 'done'}) |
|||
|
|||
@api.multi |
|||
def cancel(self): |
|||
""" Reject the change """ |
|||
if any(change.state == 'done' for change in self): |
|||
raise exceptions.Warning( |
|||
_('This change has already be applied.') |
|||
) |
|||
self.set_old_value() |
|||
self.write({'state': 'cancel'}) |
|||
|
|||
@api.model |
|||
def _has_field_changed(self, record, field, value): |
|||
field_def = record._fields[field] |
|||
current_value = field_def.convert_to_write(record[field]) |
|||
if not (current_value or value): |
|||
return False |
|||
return current_value != value |
|||
|
|||
@api.multi |
|||
def _convert_value_for_write(self, value): |
|||
if not value: |
|||
return 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 _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 field_name: name of the modified field |
|||
:param value: if no value is given, it is read from the record |
|||
""" |
|||
field_def = record._fields[field_name] |
|||
if value is _NO_VALUE: |
|||
# when the value is read from the record, we need to prepare |
|||
# it for the write (e.g. extract .id from a many2one record) |
|||
value = field_def.convert_to_write(record[field_name]) |
|||
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_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 changeset 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 |
|||
""" |
|||
new_field_name = self.get_field_for_type(rule.field_id, 'new') |
|||
new_value = self._value_for_changeset(record, field_name, value=value) |
|||
change = { |
|||
new_field_name: new_value, |
|||
'field_id': rule.field_id.id, |
|||
} |
|||
if rule.action == 'auto': |
|||
change['state'] = 'done' |
|||
pop_value = False |
|||
elif rule.action == 'validate': |
|||
change['state'] = 'draft' |
|||
pop_value = True # change to apply manually |
|||
elif rule.action == 'never': |
|||
change['state'] = 'cancel' |
|||
pop_value = True # change never applied |
|||
|
|||
if change['state'] in ('cancel', 'done'): |
|||
# Normally the 'old' value is set when we use the 'apply' |
|||
# button, but since we short circuit the 'apply', we |
|||
# directly set the 'old' value here |
|||
old_field_name = self.get_field_for_type(rule.field_id, 'old') |
|||
# get values ready to write as expected by the changeset |
|||
# (for instance, a many2one is written in a reference |
|||
# field) |
|||
origin_value = self._value_for_changeset(record, field_name) |
|||
change[old_field_name] = origin_value |
|||
|
|||
return change, pop_value |
|||
|
|||
def fields_view_get(self, *args, **kwargs): |
|||
_super = super(ResPartnerChangesetChange, self) |
|||
result = _super.fields_view_get(*args, **kwargs) |
|||
if result['type'] != 'form': |
|||
return |
|||
doc = etree.XML(result['arch']) |
|||
for suffix, ftypes in self._suffix_to_types.iteritems(): |
|||
for prefix in ('origin', 'old', 'new'): |
|||
field_name = '%s_value_%s' % (prefix, suffix) |
|||
field_nodes = doc.xpath("//field[@name='%s']" % field_name) |
|||
for node in field_nodes: |
|||
node.set( |
|||
'attrs', |
|||
"{'invisible': " |
|||
"[('field_type', 'not in', %s)]}" % (ftypes,) |
|||
) |
|||
setup_modifiers(node) |
|||
result['arch'] = etree.tostring(doc) |
|||
return result |
@ -0,0 +1,10 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
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 |
@ -0,0 +1,29 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<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> |
|||
</record> |
|||
|
|||
<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> |
|||
</record> |
|||
|
|||
</data> |
|||
|
|||
<data noupdate="1"> |
|||
|
|||
<record id="group_changeset_manager" model="res.groups"> |
|||
<field name="users" eval="[(4, ref('base.user_root'))]"/> |
|||
</record> |
|||
|
|||
<record id="group_changeset_user" model="res.groups"> |
|||
<field name="implied_ids" eval="[(4, ref('group_changeset_manager'))]"/> |
|||
<field name="users" eval="[(4, ref('base.user_root'))]"/> |
|||
</record> |
|||
|
|||
</data> |
|||
</openerp> |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
After Width: 1252 | Height: 577 | Size: 85 KiB |
After Width: 996 | Height: 557 | Size: 60 KiB |
@ -0,0 +1,6 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from . import test_changeset_flow |
|||
from . import test_changeset_field_type |
|||
from . import test_changeset_origin |
|||
from . import test_changeset_field_rule |
@ -0,0 +1,77 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
|
|||
class ChangesetMixin(object): |
|||
|
|||
def assert_changeset(self, partner, expected_source, expected_changes): |
|||
""" Check if a changeset has been created according to expected values |
|||
|
|||
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, |
|||
new_value, state) |
|||
|
|||
:param partner: record of partner having a changeset |
|||
:param expected_changes: contains tuples with the changes |
|||
:type expected_changes: list(tuple)) |
|||
""" |
|||
changeset = self.env['res.partner.changeset'].search( |
|||
[('partner_id', '=', partner.id)], |
|||
) |
|||
self.assertEqual(len(changeset), 1, |
|||
"1 changeset expected, got %s" % (changeset,)) |
|||
self.assertEqual(changeset.source, expected_source) |
|||
changes = changeset.change_ids |
|||
missing = [] |
|||
for expected_change in expected_changes: |
|||
for change in changes: |
|||
if (change.field_id, |
|||
change.get_origin_value(), |
|||
change.get_new_value(), |
|||
change.state) == expected_change: |
|||
changes -= change |
|||
break |
|||
else: |
|||
missing.append(expected_change) |
|||
message = u'' |
|||
for field, origin_value, new_value, state in missing: |
|||
message += ("- field: '%s', origin_value: '%s', " |
|||
"new_value: '%s', state: '%s'\n" % |
|||
(field.name, origin_value, new_value, state)) |
|||
for change in changes: |
|||
message += ("+ field: '%s', origin_value: '%s', " |
|||
"new_value: '%s', state: '%s'\n" % |
|||
(change.field_id.name, |
|||
change.get_origin_value(), |
|||
change.get_new_value(), |
|||
change.state)) |
|||
if message: |
|||
raise AssertionError('Changes do not match\n\n:%s' % message) |
|||
|
|||
def _create_changeset(self, partner, changes): |
|||
""" Create a changeset and its associated changes |
|||
|
|||
:param partner: 'res.partner' record |
|||
:param changes: list of changes [(field, new value, state)] |
|||
:returns: 'res.partner.changeset' record |
|||
""" |
|||
ChangesetChange = self.env['res.partner.changeset.change'] |
|||
get_field = ChangesetChange.get_field_for_type |
|||
change_values = [] |
|||
for field, value, state in changes: |
|||
change = { |
|||
'field_id': field.id, |
|||
# write in the field of the appropriate type for the |
|||
# origin field (char, many2one, ...) |
|||
get_field(field, 'new'): value, |
|||
'state': state, |
|||
} |
|||
change_values.append((0, 0, change)) |
|||
values = { |
|||
'partner_id': partner.id, |
|||
'change_ids': change_values, |
|||
} |
|||
return self.env['res.partner.changeset'].create(values) |
@ -0,0 +1,78 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from openerp.tests import common |
|||
|
|||
|
|||
class TestChangesetFieldRule(common.TransactionCase): |
|||
|
|||
def setUp(self): |
|||
super(TestChangesetFieldRule, self).setUp() |
|||
self.company_model_id = self.env.ref('base.model_res_company').id |
|||
self.field_name = self.env.ref('base.field_res_partner_name') |
|||
self.field_street = self.env.ref('base.field_res_partner_street') |
|||
|
|||
def test_get_rules(self): |
|||
ChangesetFieldRule = self.env['changeset.field.rule'] |
|||
ChangesetFieldRule.search([]).unlink() |
|||
rule1 = ChangesetFieldRule.create({ |
|||
'field_id': self.field_name.id, |
|||
'action': 'validate', |
|||
}) |
|||
rule2 = ChangesetFieldRule.create({ |
|||
'field_id': self.field_street.id, |
|||
'action': 'never', |
|||
}) |
|||
get_rules = ChangesetFieldRule.get_rules(None) |
|||
self.assertEqual(get_rules, {'name': rule1, 'street': rule2}) |
|||
|
|||
def test_get_rules_source(self): |
|||
ChangesetFieldRule = self.env['changeset.field.rule'] |
|||
ChangesetFieldRule.search([]).unlink() |
|||
rule1 = ChangesetFieldRule.create({ |
|||
'field_id': self.field_name.id, |
|||
'action': 'validate', |
|||
}) |
|||
rule2 = ChangesetFieldRule.create({ |
|||
'field_id': self.field_street.id, |
|||
'action': 'never', |
|||
}) |
|||
rule3 = ChangesetFieldRule.create({ |
|||
'source_model_id': self.company_model_id, |
|||
'field_id': self.field_street.id, |
|||
'action': 'never', |
|||
}) |
|||
model = ChangesetFieldRule |
|||
rules = model.get_rules(None) |
|||
self.assertEqual(rules, {u'name': rule1, u'street': rule2}) |
|||
rules = model.get_rules('res.company') |
|||
self.assertEqual(rules, {u'name': rule1, u'street': rule3}) |
|||
|
|||
def test_get_rules_cache(self): |
|||
ChangesetFieldRule = self.env['changeset.field.rule'] |
|||
ChangesetFieldRule.search([]).unlink() |
|||
rule = ChangesetFieldRule.create({ |
|||
'field_id': self.field_name.id, |
|||
'action': 'validate', |
|||
}) |
|||
self.assertEqual( |
|||
ChangesetFieldRule.get_rules(None)['name'].action, |
|||
'validate', |
|||
) |
|||
# Write on cursor to bypass the cache invalidation for the |
|||
# matter of the test |
|||
self.env.cr.execute("UPDATE changeset_field_rule " |
|||
"SET action = 'never' " |
|||
"WHERE id = %s", (rule.id,)) |
|||
self.assertEqual( |
|||
ChangesetFieldRule.get_rules(None)['name'].action, |
|||
'validate', |
|||
) |
|||
rule.action = 'auto' |
|||
self.assertEqual( |
|||
ChangesetFieldRule.get_rules(None)['name'].action, |
|||
'auto', |
|||
) |
|||
rule.unlink() |
|||
self.assertFalse(ChangesetFieldRule.get_rules(None)) |
@ -0,0 +1,281 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from openerp.tests import common |
|||
from .common import ChangesetMixin |
|||
|
|||
|
|||
class TestChangesetFieldType(ChangesetMixin, common.TransactionCase): |
|||
""" Check that changeset changes are stored expectingly to their types """ |
|||
|
|||
def _setup_rules(self): |
|||
ChangesetFieldRule = self.env['changeset.field.rule'] |
|||
ChangesetFieldRule.search([]).unlink() |
|||
fields = (('char', 'ref'), |
|||
('text', 'comment'), |
|||
('boolean', 'customer'), |
|||
('date', 'date'), |
|||
('integer', 'color'), |
|||
('float', 'credit_limit'), |
|||
('selection', 'type'), |
|||
('many2one', 'country_id'), |
|||
('many2many', 'category_id'), |
|||
('one2many', 'user_ids'), |
|||
('binary', 'image'), |
|||
) |
|||
for field_type, field in fields: |
|||
attr_name = 'field_%s' % field_type |
|||
field_record = self.env['ir.model.fields'].search([ |
|||
('model', '=', 'res.partner'), |
|||
('name', '=', field), |
|||
]) |
|||
# set attribute such as 'self.field_char' is a |
|||
# ir.model.fields record of the field res_partner.ref |
|||
setattr(self, attr_name, field_record) |
|||
ChangesetFieldRule.create({ |
|||
'field_id': field_record.id, |
|||
'action': 'validate', |
|||
}) |
|||
|
|||
def setUp(self): |
|||
super(TestChangesetFieldType, self).setUp() |
|||
self._setup_rules() |
|||
self.partner = self.env['res.partner'].create({ |
|||
'name': 'Original Name', |
|||
'street': 'Original Street', |
|||
}) |
|||
|
|||
def test_new_changeset_char(self): |
|||
""" Add a new changeset on a Char field """ |
|||
self.partner.write({ |
|||
self.field_char.name: 'New value', |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_char, self.partner[self.field_char.name], |
|||
'New value', 'draft'), |
|||
] |
|||
) |
|||
|
|||
def test_new_changeset_text(self): |
|||
""" Add a new changeset on a Text field """ |
|||
self.partner.write({ |
|||
self.field_text.name: 'New comment\non 2 lines', |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_text, self.partner[self.field_text.name], |
|||
'New comment\non 2 lines', 'draft'), |
|||
] |
|||
) |
|||
|
|||
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.partner.write({ |
|||
self.field_boolean.name: True, |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_boolean, self.partner[self.field_boolean.name], |
|||
True, 'draft'), |
|||
] |
|||
) |
|||
|
|||
def test_new_changeset_date(self): |
|||
""" Add a new changeset on a Date field """ |
|||
self.partner.write({ |
|||
self.field_date.name: '2015-09-15', |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_date, self.partner[self.field_date.name], |
|||
'2015-09-15', 'draft'), |
|||
] |
|||
) |
|||
|
|||
def test_new_changeset_integer(self): |
|||
""" Add a new changeset on a Integer field """ |
|||
self.partner.write({ |
|||
self.field_integer.name: 42, |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_integer, self.partner[self.field_integer.name], |
|||
42, 'draft'), |
|||
] |
|||
) |
|||
|
|||
def test_new_changeset_float(self): |
|||
""" Add a new changeset on a Float field """ |
|||
self.partner.write({ |
|||
self.field_float.name: 3.1415, |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_float, self.partner[self.field_float.name], |
|||
3.1415, 'draft'), |
|||
] |
|||
) |
|||
|
|||
def test_new_changeset_selection(self): |
|||
""" Add a new changeset on a Selection field """ |
|||
self.partner.write({ |
|||
self.field_selection.name: 'delivery', |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_selection, self.partner[self.field_selection.name], |
|||
'delivery', 'draft'), |
|||
] |
|||
) |
|||
|
|||
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.partner.write({ |
|||
self.field_many2one.name: self.env.ref('base.ch').id, |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_many2one, self.partner[self.field_many2one.name], |
|||
self.env.ref('base.ch'), 'draft'), |
|||
] |
|||
) |
|||
|
|||
def test_new_changeset_many2many(self): |
|||
""" Add a new changeset on a Many2many field is not supported """ |
|||
with self.assertRaises(NotImplementedError): |
|||
self.partner.write({ |
|||
self.field_many2many.name: [self.env.ref('base.ch').id], |
|||
}) |
|||
|
|||
def test_new_changeset_one2many(self): |
|||
""" Add a new changeset on a One2many field is not supported """ |
|||
with self.assertRaises(NotImplementedError): |
|||
self.partner.write({ |
|||
self.field_one2many.name: [self.env.ref('base.user_root').id], |
|||
}) |
|||
|
|||
def test_new_changeset_binary(self): |
|||
""" Add a new changeset on a Binary field is not supported """ |
|||
with self.assertRaises(NotImplementedError): |
|||
self.partner.write({ |
|||
self.field_binary.name: 'xyz', |
|||
}) |
|||
|
|||
def test_apply_char(self): |
|||
""" Apply a change on a Char field """ |
|||
changes = [(self.field_char, 'New Ref', 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner[self.field_char.name], 'New Ref') |
|||
|
|||
def test_apply_text(self): |
|||
""" Apply a change on a Text field """ |
|||
changes = [(self.field_text, 'New comment\non 2 lines', 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner[self.field_text.name], |
|||
'New comment\non 2 lines') |
|||
|
|||
def test_apply_boolean(self): |
|||
""" Apply a change on a Boolean field """ |
|||
# ensure the changeset has to change the value |
|||
self.partner.write({self.field_boolean.name: False}) |
|||
|
|||
changes = [(self.field_boolean, True, 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner[self.field_boolean.name], True) |
|||
|
|||
# Cannot do this while it is on the same transaction. The cache may not |
|||
# be updated |
|||
# changes = [(self.field_boolean, False, 'draft')] |
|||
# changeset = self._create_changeset(self.partner, changes) |
|||
# changeset.change_ids.apply() |
|||
# self.assertEqual(self.partner[self.field_boolean.name], False) |
|||
|
|||
def test_apply_date(self): |
|||
""" Apply a change on a Date field """ |
|||
changes = [(self.field_date, '2015-09-15', 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertAlmostEqual(self.partner[self.field_date.name], |
|||
'2015-09-15') |
|||
|
|||
def test_apply_integer(self): |
|||
""" Apply a change on a Integer field """ |
|||
changes = [(self.field_integer, 42, 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertAlmostEqual(self.partner[self.field_integer.name], 42) |
|||
|
|||
def test_apply_float(self): |
|||
""" Apply a change on a Float field """ |
|||
changes = [(self.field_float, 52.47, 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertAlmostEqual(self.partner[self.field_float.name], 52.47) |
|||
|
|||
def test_apply_selection(self): |
|||
""" Apply a change on a Selection field """ |
|||
changes = [(self.field_selection, 'delivery', 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertAlmostEqual(self.partner[self.field_selection.name], |
|||
'delivery') |
|||
|
|||
def test_apply_many2one(self): |
|||
""" Apply a change on a Many2one field """ |
|||
self.partner.with_context(__no_changeset=True).write({ |
|||
self.field_many2one.name: self.env.ref('base.fr').id, |
|||
|
|||
}) |
|||
changes = [(self.field_many2one, |
|||
'res.country,%d' % self.env.ref('base.ch').id, |
|||
'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner[self.field_many2one.name], |
|||
self.env.ref('base.ch')) |
|||
|
|||
def test_apply_many2many(self): |
|||
""" Apply a change on a Many2many field is not supported """ |
|||
changes = [(self.field_many2many, |
|||
self.env.ref('base.ch').id, |
|||
'draft')] |
|||
with self.assertRaises(NotImplementedError): |
|||
self._create_changeset(self.partner, changes) |
|||
|
|||
def test_apply_one2many(self): |
|||
""" Apply a change on a One2many field is not supported """ |
|||
changes = [(self.field_one2many, |
|||
[self.env.ref('base.user_root').id, |
|||
self.env.ref('base.user_demo').id, |
|||
], |
|||
'draft')] |
|||
with self.assertRaises(NotImplementedError): |
|||
self._create_changeset(self.partner, changes) |
|||
|
|||
def test_apply_binary(self): |
|||
""" Apply a change on a Binary field is not supported """ |
|||
changes = [(self.field_one2many, '', 'draft')] |
|||
with self.assertRaises(NotImplementedError): |
|||
self._create_changeset(self.partner, changes) |
@ -0,0 +1,246 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from datetime import datetime, timedelta |
|||
|
|||
from openerp import fields, exceptions |
|||
from openerp.tests import common |
|||
from .common import ChangesetMixin |
|||
|
|||
|
|||
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 |
|||
suite, so we only use 'char' fields. We have to ensure that the |
|||
general changeset flows work as expected, that is: |
|||
|
|||
* create a changeset when a manual/system write is made on partner |
|||
* create a changeset according to the changeset rules when a source model |
|||
is specified |
|||
* 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 |
|||
* when all the changes are either 'cancel' or 'done', the changeset |
|||
becomes 'done' |
|||
""" |
|||
|
|||
def _setup_rules(self): |
|||
ChangesetFieldRule = self.env['changeset.field.rule'] |
|||
ChangesetFieldRule.search([]).unlink() |
|||
self.field_name = self.env.ref('base.field_res_partner_name') |
|||
self.field_street = self.env.ref('base.field_res_partner_street') |
|||
self.field_street2 = self.env.ref('base.field_res_partner_street2') |
|||
ChangesetFieldRule.create({ |
|||
'field_id': self.field_name.id, |
|||
'action': 'auto', |
|||
}) |
|||
ChangesetFieldRule.create({ |
|||
'field_id': self.field_street.id, |
|||
'action': 'validate', |
|||
}) |
|||
ChangesetFieldRule.create({ |
|||
'field_id': self.field_street2.id, |
|||
'action': 'never', |
|||
}) |
|||
|
|||
def setUp(self): |
|||
super(TestChangesetFlow, self).setUp() |
|||
self._setup_rules() |
|||
self.partner = self.env['res.partner'].create({ |
|||
'name': 'X', |
|||
'street': 'street X', |
|||
'street2': 'street2 X', |
|||
}) |
|||
|
|||
def test_new_changeset(self): |
|||
""" Add a new changeset on a partner |
|||
|
|||
A new changeset is created when we write on a partner |
|||
""" |
|||
self.partner.write({ |
|||
'name': 'Y', |
|||
'street': 'street Y', |
|||
'street2': 'street2 Y', |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_name, 'X', 'Y', 'done'), |
|||
(self.field_street, 'street X', 'street Y', 'draft'), |
|||
(self.field_street2, 'street2 X', 'street2 Y', 'cancel'), |
|||
], |
|||
) |
|||
self.assertEqual(self.partner.name, 'Y') |
|||
self.assertEqual(self.partner.street, 'street X') |
|||
self.assertEqual(self.partner.street2, 'street2 X') |
|||
|
|||
def test_new_changeset_empty_value(self): |
|||
""" Create a changeset change that empty a value """ |
|||
self.partner.write({ |
|||
'street': False, |
|||
}) |
|||
self.assert_changeset( |
|||
self.partner, |
|||
self.env.user, |
|||
[(self.field_street, 'street X', False, 'draft')] |
|||
) |
|||
|
|||
def test_no_changeset_empty_value_both_sides(self): |
|||
""" No changeset created when both sides have an empty value """ |
|||
# we have to ensure that even if we write '' to a False field, we won't |
|||
# write a changeset |
|||
self.partner.with_context(__no_changeset=True).write({ |
|||
'street': False, |
|||
}) |
|||
self.partner.write({ |
|||
'street': '', |
|||
}) |
|||
self.assertFalse(self.partner.changeset_ids) |
|||
|
|||
def test_apply_change(self): |
|||
""" Apply a changeset change on a partner """ |
|||
changes = [ |
|||
(self.field_name, 'Y', 'draft'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner.name, 'Y') |
|||
self.assertEqual(changeset.change_ids.state, 'done') |
|||
|
|||
def test_apply_done_change(self): |
|||
""" Done changes do not apply (already applied) """ |
|||
changes = [ |
|||
(self.field_name, 'Y', 'done'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner.name, 'X') |
|||
|
|||
def test_apply_cancel_change(self): |
|||
""" Cancel changes do not apply """ |
|||
changes = [ |
|||
(self.field_name, 'Y', 'cancel'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner.name, 'X') |
|||
|
|||
def test_apply_empty_value(self): |
|||
""" Apply a change that empty a value """ |
|||
changes = [ |
|||
(self.field_street, False, 'draft'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertFalse(self.partner.street) |
|||
|
|||
def test_apply_change_loop(self): |
|||
""" Test @api.multi on the changes """ |
|||
changes = [ |
|||
(self.field_name, 'Y', 'draft'), |
|||
(self.field_street, 'street Y', 'draft'), |
|||
(self.field_street2, 'street2 Y', 'draft'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(self.partner.name, 'Y') |
|||
self.assertEqual(self.partner.street, 'street Y') |
|||
self.assertEqual(self.partner.street2, 'street2 Y') |
|||
|
|||
def test_apply(self): |
|||
""" Apply a full changeset on a partner """ |
|||
changes = [ |
|||
(self.field_name, 'Y', 'draft'), |
|||
(self.field_street, 'street Y', 'draft'), |
|||
(self.field_street2, 'street2 Y', 'draft'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
changeset.apply() |
|||
self.assertEqual(self.partner.name, 'Y') |
|||
self.assertEqual(self.partner.street, 'street Y') |
|||
self.assertEqual(self.partner.street2, 'street2 Y') |
|||
|
|||
def test_changeset_state_on_done(self): |
|||
""" Check that changeset state becomes done when changes are done """ |
|||
changes = [(self.field_name, 'Y', 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
self.assertEqual(changeset.state, 'draft') |
|||
changeset.change_ids.apply() |
|||
self.assertEqual(changeset.state, 'done') |
|||
|
|||
def test_changeset_state_on_cancel(self): |
|||
""" Check that rev. state becomes done when changes are canceled """ |
|||
changes = [(self.field_name, 'Y', 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
self.assertEqual(changeset.state, 'draft') |
|||
changeset.change_ids.cancel() |
|||
self.assertEqual(changeset.state, 'done') |
|||
|
|||
def test_changeset_state(self): |
|||
""" Check that changeset state becomes done with multiple changes """ |
|||
changes = [ |
|||
(self.field_name, 'Y', 'draft'), |
|||
(self.field_street, 'street Y', 'draft'), |
|||
(self.field_street2, 'street2 Y', 'draft'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
self.assertEqual(changeset.state, 'draft') |
|||
changeset.apply() |
|||
self.assertEqual(changeset.state, 'done') |
|||
|
|||
def test_apply_changeset_with_other_pending(self): |
|||
""" Error when applying when previous pending changesets exist """ |
|||
changes = [(self.field_name, 'Y', 'draft')] |
|||
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 |
|||
old_changeset.date = to_string(datetime.now() - timedelta(days=1)) |
|||
changes = [(self.field_name, 'Z', 'draft')] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
with self.assertRaises(exceptions.Warning): |
|||
changeset.change_ids.apply() |
|||
|
|||
def test_apply_different_changesets(self): |
|||
""" Apply different changesets at once """ |
|||
partner2 = self.env['res.partner'].create({'name': 'P2'}) |
|||
changes = [ |
|||
(self.field_name, 'Y', 'draft'), |
|||
(self.field_street, 'street Y', 'draft'), |
|||
(self.field_street2, 'street2 Y', 'draft'), |
|||
] |
|||
changeset = self._create_changeset(self.partner, changes) |
|||
self.assertEqual(self.partner.count_pending_changesets, 1) |
|||
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.street, 'street Y') |
|||
self.assertEqual(self.partner.street2, 'street2 Y') |
|||
self.assertEqual(partner2.name, 'Y') |
|||
self.assertEqual(partner2.street, 'street Y') |
|||
self.assertEqual(partner2.street2, 'street2 Y') |
|||
self.assertEqual(changeset.state, 'done') |
|||
self.assertEqual(changeset2.state, 'done') |
|||
|
|||
def test_new_changeset_source(self): |
|||
""" Source is the user who made the change """ |
|||
self.partner.write({ |
|||
'street': False, |
|||
}) |
|||
changeset = self.partner.changeset_ids |
|||
self.assertEqual(changeset.source, self.env.user) |
|||
|
|||
def test_new_changeset_source_other_model(self): |
|||
""" Define source from another model """ |
|||
company = self.env.ref('base.main_company') |
|||
keys = { |
|||
'__changeset_rules_source_model': 'res.company', |
|||
'__changeset_rules_source_id': company.id, |
|||
} |
|||
self.partner.with_context(**keys).write({ |
|||
'street': False, |
|||
}) |
|||
changeset = self.partner.changeset_ids |
|||
self.assertEqual(changeset.source, company) |
@ -0,0 +1,118 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2015-2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from openerp.tests import common |
|||
from .common import ChangesetMixin |
|||
|
|||
|
|||
class TestChangesetOrigin(ChangesetMixin, common.TransactionCase): |
|||
""" Check that origin - old fields are stored as expected. |
|||
|
|||
'origin' fields dynamically read fields from the partner when the state |
|||
of the change is 'draft'. Once a change becomes 'done' or 'cancel', the |
|||
'old' field copies the value from the partner and then the 'origin' field |
|||
displays the 'old' value. |
|||
""" |
|||
|
|||
def _setup_rules(self): |
|||
ChangesetFieldRule = self.env['changeset.field.rule'] |
|||
ChangesetFieldRule.search([]).unlink() |
|||
self.field_name = self.env.ref('base.field_res_partner_name') |
|||
ChangesetFieldRule.create({ |
|||
'field_id': self.field_name.id, |
|||
'action': 'validate', |
|||
}) |
|||
|
|||
def setUp(self): |
|||
super(TestChangesetOrigin, self).setUp() |
|||
self._setup_rules() |
|||
self.partner = self.env['res.partner'].create({ |
|||
'name': 'X', |
|||
}) |
|||
|
|||
def test_origin_value_of_change_with_apply(self): |
|||
""" Origin field is read from the parter or 'old' - with apply |
|||
|
|||
According to the state of the change. |
|||
""" |
|||
self.partner.write({ |
|||
'name': 'Y', |
|||
}) |
|||
changeset = self.partner.changeset_ids |
|||
change = changeset.change_ids |
|||
self.assertEqual(self.partner.name, 'X') |
|||
self.assertEqual(change.origin_value_char, 'X') |
|||
self.assertEqual(change.origin_value_display, 'X') |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'A'}) |
|||
# depends cannot trigger all fileds from partner. In real use case, |
|||
# the user will probably be in different transaction, he will get the |
|||
# new value of the field |
|||
self.partner.invalidate_cache() |
|||
self.assertEqual(change.origin_value_char, 'A') |
|||
self.assertEqual(change.origin_value_display, 'A') |
|||
change.apply() |
|||
self.assertEqual(change.origin_value_char, 'A') |
|||
self.assertEqual(change.origin_value_display, 'A') |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'B'}) |
|||
self.assertEqual(change.origin_value_char, 'A') |
|||
self.assertEqual(change.origin_value_display, 'A') |
|||
|
|||
def test_origin_value_of_change_with_cancel(self): |
|||
""" Origin field is read from the parter or 'old' - with cancel |
|||
|
|||
According to the state of the change. |
|||
""" |
|||
self.partner.write({ |
|||
'name': 'Y', |
|||
}) |
|||
changeset = self.partner.changeset_ids |
|||
change = changeset.change_ids |
|||
self.assertEqual(self.partner.name, 'X') |
|||
self.assertEqual(change.origin_value_char, 'X') |
|||
self.assertEqual(change.origin_value_display, 'X') |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'A'}) |
|||
# depends cannot trigger all fileds from partner. In real use case, |
|||
# the user will probably be in different transaction, he will get the |
|||
# new value of the field |
|||
self.partner.invalidate_cache() |
|||
self.assertEqual(change.origin_value_char, 'A') |
|||
self.assertEqual(change.origin_value_display, 'A') |
|||
change.cancel() |
|||
self.assertEqual(change.origin_value_char, 'A') |
|||
self.assertEqual(change.origin_value_display, 'A') |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'B'}) |
|||
self.assertEqual(change.origin_value_char, 'A') |
|||
self.assertEqual(change.origin_value_display, 'A') |
|||
|
|||
def test_old_field_of_change_with_apply(self): |
|||
""" Old field is stored when the change is applied """ |
|||
self.partner.write({ |
|||
'name': 'Y', |
|||
}) |
|||
changeset = self.partner.changeset_ids |
|||
change = changeset.change_ids |
|||
self.assertEqual(self.partner.name, 'X') |
|||
self.assertFalse(change.old_value_char) |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'A'}) |
|||
self.assertFalse(change.old_value_char) |
|||
change.apply() |
|||
self.assertEqual(change.old_value_char, 'A') |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'B'}) |
|||
self.assertEqual(change.old_value_char, 'A') |
|||
|
|||
def test_old_field_of_change_with_cancel(self): |
|||
""" Old field is stored when the change is canceled """ |
|||
self.partner.write({ |
|||
'name': 'Y', |
|||
}) |
|||
changeset = self.partner.changeset_ids |
|||
change = changeset.change_ids |
|||
self.assertEqual(self.partner.name, 'X') |
|||
self.assertFalse(change.old_value_char) |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'A'}) |
|||
self.assertFalse(change.old_value_char) |
|||
change.cancel() |
|||
self.assertEqual(change.old_value_char, 'A') |
|||
self.partner.with_context(__no_changeset=True).write({'name': 'B'}) |
|||
self.assertEqual(change.old_value_char, 'A') |
@ -0,0 +1,61 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<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"> |
|||
<tree string="Changeset Fields Rules"> |
|||
<field name="field_id"/> |
|||
<field name="source_model_id"/> |
|||
<field name="action"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<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"> |
|||
<form string="Changeset Fields Rules"> |
|||
<sheet string="Changeset Fields Rules"> |
|||
<group> |
|||
<field name="field_id" |
|||
options="{'no_create_edit': True, 'no_open': True}" |
|||
/> |
|||
<field name="action"/> |
|||
<field name="source_model_id" |
|||
widget="selection" /> |
|||
</group> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<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"> |
|||
<search string="Changeset Fields Rules"> |
|||
<field name="field_id"/> |
|||
<field name="source_model_id"/> |
|||
<field name="action"/> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
|
|||
<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="res_model">changeset.field.rule</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="search_view_id" ref="view_changeset_field_rule_search"/> |
|||
</record> |
|||
|
|||
<menuitem id="menu_changeset_field_rule" |
|||
parent="menu_changeset" |
|||
name="Field Rules" |
|||
groups="group_changeset_manager" |
|||
sequence="20" |
|||
action="action_changeset_field_rule_view"/> |
|||
</odoo> |
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<menuitem id="menu_changeset" |
|||
name="Partner Changesets" |
|||
groups="group_changeset_user" |
|||
parent="base.menu_sales_config" |
|||
sequence="20"/> |
|||
</odoo> |
@ -0,0 +1,157 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<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"> |
|||
<tree string="Partner Changeset" delete="false" create="false"> |
|||
<field name="partner_id"/> |
|||
<field name="date"/> |
|||
<field name="state"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<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"> |
|||
<form string="Partner Changeset" delete="false" create="false"> |
|||
<header> |
|||
<button name="apply" |
|||
string="Apply pending changes" type="object" |
|||
class="oe_highlight" |
|||
states="draft"/> |
|||
<button name="cancel" |
|||
string="Reject pending changes" type="object" |
|||
class="oe_highlight" |
|||
states="draft"/> |
|||
<field name="state" widget="statusbar" |
|||
statusbar_visible="draft,done"/> |
|||
</header> |
|||
<sheet string="Partner Changeset"> |
|||
<group> |
|||
<field name="partner_id"/> |
|||
<field name="source"/> |
|||
<field name="date"/> |
|||
</group> |
|||
<group string="Changes"> |
|||
<field name="change_ids" nolabel="1"> |
|||
<tree string="Partner Changeset Change"> |
|||
<field name="field_id" context="{'no_open': true}"/> |
|||
<field name="field_type" invisible="1"/> |
|||
|
|||
<field name="origin_value_display" string="Previous"/> |
|||
<field name="new_value_display"/> |
|||
|
|||
<field name="state"/> |
|||
<button name="apply" |
|||
string="Apply" type="object" |
|||
icon="gtk-apply" |
|||
states="draft"/> |
|||
<button name="cancel" |
|||
string="Reject" type="object" |
|||
icon="gtk-close" |
|||
states="draft"/> |
|||
</tree> |
|||
</field> |
|||
</group> |
|||
<group> |
|||
<field name="note"/> |
|||
</group> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<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"> |
|||
<form string="Partner Changeset Change" delete="false" create="false"> |
|||
<header> |
|||
<button name="apply" |
|||
string="Apply" type="object" |
|||
class="oe_highlight" |
|||
states="draft"/> |
|||
<button name="cancel" |
|||
string="Reject" type="object" |
|||
class="oe_highlight" |
|||
states="draft"/> |
|||
<field name="state" widget="statusbar" |
|||
statusbar_visible="draft,done"/> |
|||
</header> |
|||
<sheet> |
|||
<group> |
|||
<field name="field_id" options="{'no_open': true}"/> |
|||
<field name="field_type" invisible="1"/> |
|||
|
|||
<!-- attrs are added in fields_view_get --> |
|||
<field name="origin_value_char"/> |
|||
<field name="new_value_char"/> |
|||
|
|||
<field name="origin_value_date"/> |
|||
<field name="new_value_date"/> |
|||
|
|||
<field name="origin_value_datetime"/> |
|||
<field name="new_value_datetime"/> |
|||
|
|||
<field name="origin_value_float"/> |
|||
<field name="new_value_float"/> |
|||
|
|||
<field name="origin_value_integer"/> |
|||
<field name="new_value_integer"/> |
|||
|
|||
<field name="origin_value_text"/> |
|||
<field name="new_value_text"/> |
|||
|
|||
<field name="origin_value_boolean"/> |
|||
<field name="new_value_boolean"/> |
|||
|
|||
<field name="origin_value_reference"/> |
|||
<field name="new_value_reference"/> |
|||
|
|||
</group> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<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"> |
|||
<search string="Partner Changeset"> |
|||
<field name="partner_id"/> |
|||
<filter string="Pending" name="draft" |
|||
domain="[('state','=','draft')]"/> |
|||
<filter string="Done" name="done" |
|||
domain="[('state','=','done')]"/> |
|||
<group expand="0" string="Group By"> |
|||
<filter string="Partner" |
|||
name="groupby_partner_id" |
|||
context="{'group_by': 'partner_id'}"/> |
|||
<filter string="State" |
|||
name="groupby_state" |
|||
context="{'group_by': 'state'}"/> |
|||
</group> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
|
|||
<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="res_model">res.partner.changeset</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="context">{'search_default_draft': 1}</field> |
|||
<field name="search_view_id" ref="view_res_partner_changeset_search"/> |
|||
</record> |
|||
|
|||
<menuitem id="menu_res_partner_changeset" |
|||
parent="menu_changeset" |
|||
sequence="20" |
|||
name="Changesets" |
|||
action="action_res_partner_changeset_view"/> |
|||
</odoo> |
@ -0,0 +1,37 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<record id="res_partner_view_buttons" model="ir.ui.view"> |
|||
<field name="name">res.partner.view.buttons</field> |
|||
<field name="model">res.partner</field> |
|||
<field name="inherit_id" ref="base.view_partner_form" /> |
|||
<field name="priority" eval="18"/> |
|||
<field name="groups_id" eval="[(4, ref('partner_changeset.group_changeset_user'))]"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//div[@name='button_box']" position="inside"> |
|||
<button class="oe_inline oe_stat_button" |
|||
type="action" |
|||
name="%(partner_changeset.action_res_partner_changeset_view)d" |
|||
context="{'search_default_draft': 1, 'search_default_partner_id': active_id}" |
|||
icon="fa-code-fork"> |
|||
<field string="Changes" |
|||
name="count_pending_changesets" |
|||
widget="statinfo"/> |
|||
</button> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="view_res_partner_filter" model="ir.ui.view"> |
|||
<field name="name">res.partner.select</field> |
|||
<field name="model">res.partner</field> |
|||
<field name="inherit_id" ref="base.view_res_partner_filter" /> |
|||
<field name="arch" type="xml"> |
|||
<filter name="customer" position="after"> |
|||
<separator/> |
|||
<filter string="Pending Changesets" |
|||
name="pending_changesets" |
|||
domain="[('count_pending_changesets', '>', 0)]"/> |
|||
</filter> |
|||
</field> |
|||
</record> |
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue