[MERGE] Sync with trunk
bzr revid: tde@openerp.com-20130808083426-46cvo5e1g3x8nvgs
This commit is contained in:
commit
577f56497b
|
@ -6,6 +6,14 @@ Changelog
|
|||
`trunk`
|
||||
-------
|
||||
|
||||
- Cleaned and slightly refactored ``ir.actions.server``. The ``loop``, ``sms``
|
||||
and ``dummy`` server actions have been removed; ``object_create`` and
|
||||
``object_copy`` have been merged into ``object_create``; ``other`` is now ``multi``
|
||||
and raises in case of loops. See :ref:`ir-actions-server` for more details.
|
||||
- Removed ``sms_send`` method.
|
||||
- Added checking of recursions in many2many loops using ``_check_m2m_recursion``.
|
||||
- Added MONTHS attribute on fields.date and fields.datetime, holding the list
|
||||
(month_number, month_name)
|
||||
- Almost removed ``LocalService()``. For reports,
|
||||
``openerp.osv.orm.Model.print_report()`` can be used. For workflows, see
|
||||
:ref:`orm-workflows`.
|
||||
|
|
|
@ -14,11 +14,13 @@ OpenERP Server
|
|||
02_architecture
|
||||
03_module_dev
|
||||
04_security
|
||||
workflows
|
||||
05_test_framework
|
||||
06_misc
|
||||
deployment-gunicorn
|
||||
deployment-mod-wsgi
|
||||
form-view-guidelines
|
||||
ir_actions
|
||||
|
||||
OpenERP Command
|
||||
'''''''''''''''
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
.. _ir-actions:
|
||||
|
||||
Ir Actions
|
||||
===========
|
||||
|
||||
.. _ir-actions-server:
|
||||
|
||||
Server actions
|
||||
++++++++++++++
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
|
||||
.. currentmodule:: openerp.addons.base.ir.ir_actions
|
||||
|
||||
.. autoclass:: actions_server
|
||||
:noindex:
|
||||
|
||||
Adding a new sever action
|
||||
-------------------------
|
||||
|
||||
The ``state`` field holds the various available types of server action. In order
|
||||
to add a new server action, the first thing to do is to override the ``_get_states``
|
||||
method that returns the list of values available for the selection field.
|
||||
|
||||
.. automethod:: actions_server._get_states
|
||||
:noindex:
|
||||
|
||||
The method called when executing the server action is the ``run`` method. This
|
||||
method calls ``run_action_<STATE>``. When adding a new server action type, you
|
||||
have to define the related method that will be called upon execution.
|
||||
|
||||
.. automethod:: actions_server.run
|
||||
:noindex:
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
`8.0`
|
||||
'''''
|
||||
|
||||
The refactoring of OpenERP 8.0 server actions removed the following types of
|
||||
server action:
|
||||
|
||||
- ``loop``: can be replaced by a ``code`` action
|
||||
- ``dummy``: can be replaced by a void ``code`` action
|
||||
- ``object_create`` and ``object_copy`` have been merged into a single and
|
||||
more understandable ``object_create`` action
|
||||
- ``other`` is renamed ``multi`` and raises in case of loops
|
|
@ -0,0 +1,306 @@
|
|||
.. _workflows:
|
||||
|
||||
Workflows
|
||||
=========
|
||||
|
||||
In OpenERP, a workflow is a technical artefact to manage a set of "things to do"
|
||||
associated to the records of some data model. The workflow provides a higher-
|
||||
level way to organize the things to do on a record.
|
||||
|
||||
More specifically, a workflow is a directed graph where the nodes are called
|
||||
"activities" and the arcs are called "transitions".
|
||||
|
||||
- Activities define work that should be done within the OpenERP server, such as
|
||||
changing the state of some records, or sending emails.
|
||||
|
||||
- Transitions control how the workflow progresses from activity to activity.
|
||||
|
||||
In the definition of a workflow, one can attach conditions, signals, and
|
||||
triggers to transitions, so that the behavior of the workflow depends on user
|
||||
actions (such as clicking on a button), changes to records, or arbitrary Python
|
||||
code.
|
||||
|
||||
Basics
|
||||
------
|
||||
|
||||
Defining a workflow with data files is straightforward: a record "workflow" is
|
||||
given together with records for the activities and the transitions. For
|
||||
instance, here is a simple sequence of two activities defined in XML::
|
||||
|
||||
<record id="test_workflow" model="workflow">
|
||||
<field name="name">test.workflow</field>
|
||||
<field name="osv">test.workflow.model</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">print_a()</field>
|
||||
</record>
|
||||
<record id="activity_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">print_b()</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_a"/>
|
||||
<field name="act_to" ref="activity_b"/>
|
||||
</record>
|
||||
|
||||
A worfklow is always defined with respect to a particular model (the model is
|
||||
given by the attribute ``osv`` on the model ``workflow``). Methods specified in
|
||||
the activities or transitions will be called on that model.
|
||||
|
||||
In the example code above, a workflow called "test_workflow" is created. It is
|
||||
made up of two activies, named "a" and "b", and one transition, going from "a"
|
||||
to "b".
|
||||
|
||||
The first activity has its attribute ``flow_start`` set to ``True`` so that
|
||||
OpenERP knows where to start the workflow traversal after it is instanciated.
|
||||
Because ``on_create`` is set to True on the workflow record, the workflow is
|
||||
instanciated for each newly created record. (Otherwise, the workflow should be
|
||||
instanciated by other means, such as from some module Python code.)
|
||||
|
||||
When the workflow is instanciated, it begins with activity "a". That activity
|
||||
is of kind ``function``, which means that the action ``print_a()`` is a method
|
||||
call on the model ``test.workflow`` (the usual ``cr, uid, ids, context``
|
||||
arguments are passed for you).
|
||||
|
||||
The transition between "a" and "b" does not specify any condition. This means
|
||||
that the workflow instance immediately goes from "a" to "b" after "a" has been
|
||||
processed, and thus also processes activity "b".
|
||||
|
||||
Transitions
|
||||
-----------
|
||||
|
||||
Transitions provide the control structures to orchestrate a workflow. When an
|
||||
activity is completed, the workflow engine tries to get across transitions
|
||||
departing from the completed activity, towards the next activities. In their
|
||||
simplest form (as in the example above), they link activities sequentially:
|
||||
activities are processed as soon as the activities preceding them are completed.
|
||||
|
||||
Instead of running all activities in one fell swoop, it is also possible to wait
|
||||
on transitions, going through them only when some criteria are met. The criteria
|
||||
are the conditions, the signals, and the triggers. They are detailed in the
|
||||
following sections.
|
||||
|
||||
Conditions
|
||||
''''''''''
|
||||
|
||||
When an activity has been completed, its outgoing transitions are inspected to
|
||||
determine whether it is possible for the workflow instance to proceed through
|
||||
them and reach the next activities. When only a condition is defined (i.e., no
|
||||
signal or trigger is defined), the condition is evaluated by OpenERP, and if it
|
||||
evaluates to ``True``, the worklfow instance progresses through the transition.
|
||||
If the condition is not met, it will be reevaluated every time the associated
|
||||
record is modified, or by an explicit method call to do it.
|
||||
|
||||
By default, the attribute ``condition`` (i.e., the expression to be evaluated)
|
||||
is just "True", which trivially evaluates to ``True``. Note that the condition
|
||||
may be several lines long; in that case, the value of the last one determines
|
||||
whether the transition can be taken.
|
||||
|
||||
In the condition evaluation environment, several symbols are conveniently
|
||||
defined (in addition to the OpenERP ``safe_eval`` environment):
|
||||
|
||||
- all the model column names, and
|
||||
- all the browse record's attributes.
|
||||
|
||||
Signals
|
||||
'''''''
|
||||
|
||||
In addition to a condition, a transition can specify a signal name. When such
|
||||
a signal name is present, the transition is not taken directly, even if the
|
||||
condition evaluates to ``True``. Instead the transition blocks, waiting to be
|
||||
woken up.
|
||||
|
||||
In order to wake up a transition with a defined signal name, the signal must be
|
||||
sent to the workflow instance. A common way to send a signal is to use a button
|
||||
in the user interface, using the element ``<button/>`` with the signal name as
|
||||
the attribute ``name`` of the button. Once the button is clicked, the signal is
|
||||
sent to the workflow instance of the current record.
|
||||
|
||||
.. note:: The condition is still evaluated when the signal is sent to the
|
||||
workflow instance.
|
||||
|
||||
Triggers
|
||||
''''''''
|
||||
|
||||
With conditions that evaluate to ``False``, transitions are not taken (and thus
|
||||
the activity it leads to is not processed immediately). Still, the workflow
|
||||
instance can get new chances to progress across that transition by providing
|
||||
so-called triggers. The idea is that when the condition is not satisfied,
|
||||
triggers are recorded in database. Later, it is possible to wake up
|
||||
specifically the workflow instances that installed those triggers, offering
|
||||
them to reevaluate their transition conditions. This mechanism makes it cheaper
|
||||
to wake up workflow instances by targetting just a few of them (those that have
|
||||
installed the triggers) instead of all of them.
|
||||
|
||||
Triggers are recorded in database as record IDs (together with the model name)
|
||||
and refer to the workflow instance waiting for those records. The transition
|
||||
definition provides a model name (attribute ``trigger_model``) and a Python
|
||||
expression (attribute ``trigger_expression``) that evaluates to a list of record
|
||||
IDs in the given model. Any of those records can wake up the workflow instance
|
||||
they are associated with.
|
||||
|
||||
.. note:: Note that triggers are not re-installed whenever the transition is
|
||||
re-tried.
|
||||
|
||||
Splitting and joining transitions
|
||||
'''''''''''''''''''''''''''''''''
|
||||
|
||||
When multiple transitions leave the same activity, or lead to the same activity,
|
||||
OpenERP provides some control over which transitions are actually taken, or how
|
||||
the reached activity will be processed. The attributes ``split_mode`` and
|
||||
``join_mode`` on the activity are used for such control. The possible values of
|
||||
those attributes are explained below.
|
||||
|
||||
Activities
|
||||
----------
|
||||
|
||||
While the transitions can be seen as the control structures of the workflows,
|
||||
activities are the places where everything happens, from changing record states
|
||||
to sending email.
|
||||
|
||||
Different kinds of activities exist: ``Dummy``, ``Function``, ``Subflow``, and
|
||||
``Stop all``, each doing different things when the activity is processed. In
|
||||
addition to their kind, activies have other properties, detailed in the next
|
||||
sections.
|
||||
|
||||
Flow start and flow stop
|
||||
''''''''''''''''''''''''
|
||||
|
||||
The attribute ``flow_start`` is a boolean value specifying whether the activity
|
||||
is processed when the workflow is instanciated. Multiple activities can have
|
||||
their attribute ``flow_start`` set to ``True``. When instanciating a workflow
|
||||
for a record, OpenERP simply processes all of them, and evaluate all their
|
||||
outgoing transitions afterwards.
|
||||
|
||||
The attribute ``flow_stop`` is a boolean value specifying whether the activity
|
||||
stops the workflow instance. A workflow instance is considered completed when
|
||||
all its activities with the attribute ``flow_stop`` set to ``True`` are
|
||||
completed.
|
||||
|
||||
It is important for OpenERP to know when a workflow instance is completed. A
|
||||
workflow can have an activity that is actually another workflow (called a
|
||||
subflow); that activity is completed when the subflow is completed.
|
||||
|
||||
Subflow
|
||||
'''''''
|
||||
|
||||
An activity can embed a complete workflow, called a subflow (the embedding
|
||||
workflow is called the parent workflow). The workflow to instanciate is
|
||||
specified by attribute ``subflow_id``.
|
||||
|
||||
.. note:: In the GUI, that attribute can not be set unless the kind of the
|
||||
activity is ``Subflow``.
|
||||
|
||||
The activity is considered completed (and its outgoing transitions ready to be
|
||||
evaluated) when the subflow is completed (see attribute ``flow_stop`` above).
|
||||
|
||||
Sending a signal from a subflow
|
||||
'''''''''''''''''''''''''''''''
|
||||
|
||||
When a workflow is embedded in an activity (as a subflow) of a workflow, the
|
||||
sublow can send a signal from its own activities to the parent workflow by
|
||||
giving a signal name in the attribute ``signal_send``. OpenERP processes those
|
||||
activities by sending the value of ``signal_send`` prefixed by "subflow." to
|
||||
the parent workflow instance.
|
||||
|
||||
In other words, it is possible to react and get transitions in the parent
|
||||
workflow as activities are executed in the sublow.
|
||||
|
||||
Server actions
|
||||
''''''''''''''
|
||||
|
||||
An activity can run a "Server Action" by specifying its ID in the attribute
|
||||
``action_id``.
|
||||
|
||||
Python action
|
||||
'''''''''''''
|
||||
|
||||
An activity can execute some Python code, given by the attribute ``action``.
|
||||
The evaluation environment is the same as the one explained in the section
|
||||
`Conditions`_.
|
||||
|
||||
Split mode
|
||||
''''''''''
|
||||
|
||||
After an activity has been processed, its outgoing transitions are evaluated.
|
||||
Normally, if a transition can be taken, OpenERP traverses it and proceed to the
|
||||
activity the transition leads to.
|
||||
|
||||
Actually, when more than a single transition is leaving an activity, OpenERP may
|
||||
proceed or not, depending on the other transitions. That is, the conditions on
|
||||
the transitions can be combined together, and the combined result instructs
|
||||
OpenERP to traverse zero, one, or all the transitions. The way they are combined
|
||||
is controlled by the attribute ``split_mode``.
|
||||
|
||||
There are three possible split modes: ``XOR``, ``OR`` and ``AND``.
|
||||
|
||||
``XOR``
|
||||
When the transitions are combined with a ``XOR`` split mode, as soon as a
|
||||
transition has a satisfied condition, the transition is traversed and the
|
||||
others are skipped.
|
||||
|
||||
``OR``
|
||||
With the ``OR`` mode, all the transitions with a satisfied condition are
|
||||
traversed. The remaining transitions will not be evaluated later.
|
||||
|
||||
``AND``
|
||||
With the ``AND`` mode, OpenERP will wait for all outgoing transition
|
||||
conditions to be satisfied, then traverse all of them at once.
|
||||
|
||||
Join mode
|
||||
'''''''''
|
||||
|
||||
Just like outgoing transition conditions can be combined together to decide
|
||||
whether they can be traversed or not, incoming transitions can be combined
|
||||
together to decide if and when an activity may be processed. The attribute
|
||||
``join_mode`` controls that behavior.
|
||||
|
||||
There are two possible join modes: ``XOR`` and ``AND``.
|
||||
|
||||
``XOR``
|
||||
With the ``XOR`` mode, an incoming transition with a satisfied condition is
|
||||
traversed immediately, and enables the processing of the activity.
|
||||
|
||||
``AND``
|
||||
With the ``AND`` mode, OpenERP will wait until all incoming transitions have
|
||||
been traversed before enabling the processing of the activity.
|
||||
|
||||
Kinds
|
||||
'''''
|
||||
|
||||
Activities can be of different kinds: ``dummy``, ``function``, ``subflow``, or
|
||||
``stopall``. The kind defines what type of work an activity can do.
|
||||
|
||||
Dummy
|
||||
The ``dummy`` kind is for activities that do nothing, or for activities that
|
||||
only call a server action. Activities that do nothing can be used as hubs to
|
||||
gather/dispatch transitions.
|
||||
|
||||
Function
|
||||
The ``function`` kind is for activities that only need to run some Python
|
||||
code, and possibly a server action.
|
||||
|
||||
Stop all
|
||||
The ``stopall`` kind is for activities that will completely stop the
|
||||
workflow instance and mark it as completed. In addition they can also run
|
||||
some Python code.
|
||||
|
||||
Subflow
|
||||
When the kind of the activity is ``subflow``, the activity embeds another
|
||||
workflow instance. When the subflow is completed, the activity is also
|
||||
considered completed.
|
||||
|
||||
By default, the subflow is instanciated for the same record as the parent
|
||||
workflow. It is possible to change that behavior by providing Python code
|
||||
that returns a record ID (of the same data model as the subflow). The
|
||||
embedded subflow instance is then the one of the given record.
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:42+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:42+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
"X-Poedit-Language: Czech\n"
|
||||
|
||||
#. module: base
|
||||
|
@ -4897,7 +4897,7 @@ msgstr "Objednávka obědů, stravování, potraviny"
|
|||
#: field:ir.model.fields,required:0
|
||||
#: field:res.partner.bank.type.field,required:0
|
||||
msgid "Required"
|
||||
msgstr "Požadováno"
|
||||
msgstr "Povinné"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ro
|
||||
|
@ -7934,7 +7934,7 @@ msgstr "Zleva doprava"
|
|||
#: view:res.lang:0
|
||||
#: field:res.lang,translatable:0
|
||||
msgid "Translatable"
|
||||
msgstr "Přeložitelný"
|
||||
msgstr "Přeložitelné"
|
||||
|
||||
#. module: base
|
||||
#: help:base.language.import,code:0
|
||||
|
@ -14735,7 +14735,7 @@ msgstr ""
|
|||
#: field:ir.actions.server,trigger_obj_id:0
|
||||
#: field:ir.model.fields,relation_field:0
|
||||
msgid "Relation Field"
|
||||
msgstr "?!?Související pole"
|
||||
msgstr "Pole relace"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_portal_project
|
||||
|
@ -15029,7 +15029,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.model.fields,relation:0
|
||||
msgid "Object Relation"
|
||||
msgstr "Vztah objektu"
|
||||
msgstr "Relace objektu"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_voucher
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -8452,15 +8452,15 @@ msgstr ""
|
|||
"\n"
|
||||
"Jeder Mitarbeiter kann seine aufgewendete Zeit für verschiedene Projekte "
|
||||
"kodieren und verfolgen.\n"
|
||||
"Ein Projekt ist eine analytische Rechnung und die Zeit, die jemand an diesem "
|
||||
"Projekt verbracht hat,\n"
|
||||
"verursacht Kosten auf dem entsprechenden Konto. \n"
|
||||
"Ein Projekt wird automatisch zur Kostenstelle. Die Zeit, die jemand an "
|
||||
"diesem Projekt verbracht hat,\n"
|
||||
"verursacht dementsprechend Kosten auf dieser Projektkostenstelle. \n"
|
||||
"\n"
|
||||
"Viele Berichterstattungen über die Verfolgung von Zeit und Mitarbeiter "
|
||||
"werden bereitgestellt.\n"
|
||||
"Es ist vollständig in die Kostenrechnung integriert. Es ermöglicht Ihnen die "
|
||||
"Einrichtung einer \n"
|
||||
"Betriebsführung nach Angelegenheit.\n"
|
||||
"Das Modul bietet außerdem Auswertungen zur Rückverfolgung der Mitarbeiter "
|
||||
"Arbeitszeit in Projekten.\n"
|
||||
"Es ist dadurch vollständig in die OpenERP Kostenrechnung integriert und "
|
||||
"ermöglicht dem Management \n"
|
||||
"ein effizientes Projektcontrolling.\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
|
|
|
@ -12,8 +12,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
"X-Poedit-Country: GREECE\n"
|
||||
"X-Poedit-Language: Greek\n"
|
||||
"X-Poedit-SourceCharset: utf-8\n"
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -25,6 +25,10 @@ msgid ""
|
|||
"================================================\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Module for the Check Writing and Check Printing.\n"
|
||||
"================================================\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
@ -60,7 +64,7 @@ msgstr "View Architecture"
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_sale_stock
|
||||
msgid "Quotation, Sale Orders, Delivery & Invoicing Control"
|
||||
msgstr ""
|
||||
msgstr "Quotation, Sale Orders, Delivery & Invoicing Control"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.sequence,implementation:0
|
||||
|
@ -89,12 +93,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_point_of_sale
|
||||
msgid "Touchscreen Interface for Shops"
|
||||
msgstr ""
|
||||
msgstr "Touchscreen Interface for Shops"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
|
||||
msgid "Indian Payroll"
|
||||
msgstr ""
|
||||
msgstr "Indian Payroll"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.cron,model:0
|
||||
|
@ -138,7 +142,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.actions.client,params:0
|
||||
msgid "Supplementary arguments"
|
||||
msgstr ""
|
||||
msgstr "Supplementary arguments"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_google_base_account
|
||||
|
@ -147,11 +151,14 @@ msgid ""
|
|||
"The module adds google user in res user.\n"
|
||||
"========================================\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"The module adds google user in res user.\n"
|
||||
"========================================\n"
|
||||
|
||||
#. module: base
|
||||
#: help:res.partner,employee:0
|
||||
msgid "Check this box if this contact is an Employee."
|
||||
msgstr ""
|
||||
msgstr "Check this box if this contact is an Employee."
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model.fields,domain:0
|
||||
|
@ -182,7 +189,7 @@ msgstr "Target Window"
|
|||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_rml:0
|
||||
msgid "Main Report File Path"
|
||||
msgstr ""
|
||||
msgstr "Main Report File Path"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
|
||||
|
@ -203,6 +210,16 @@ msgid ""
|
|||
"revenue\n"
|
||||
"reports."
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Generate your Invoices from Expenses, Timesheet Entries.\n"
|
||||
"========================================================\n"
|
||||
"\n"
|
||||
"Module to generate invoices based on costs (human resources, expenses, "
|
||||
"...).\n"
|
||||
"\n"
|
||||
"You can define price lists in analytic account, make some theoretical "
|
||||
"revenue\n"
|
||||
"reports."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_sequence.py:134
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -23520,3 +23520,115 @@ msgstr "Registro"
|
|||
#~ "que hacer, y se puede concentrar en la realización efectiva de dichas "
|
||||
#~ "tareas.\n"
|
||||
#~ " "
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ "This module allows to use several analytic plans, according to the general "
|
||||
#~ "journal.\n"
|
||||
#~ "============================================================================="
|
||||
#~ "======\n"
|
||||
#~ "\n"
|
||||
#~ "Here multiple analytic lines are created when the invoice or the entries\n"
|
||||
#~ "are confirmed.\n"
|
||||
#~ "\n"
|
||||
#~ "For example, you can define the following analytic structure:\n"
|
||||
#~ " Projects\n"
|
||||
#~ " Project 1\n"
|
||||
#~ " SubProj 1.1\n"
|
||||
#~ " SubProj 1.2\n"
|
||||
#~ "\n"
|
||||
#~ " Project 2\n"
|
||||
#~ " Salesman\n"
|
||||
#~ " Eric\n"
|
||||
#~ " Fabien\n"
|
||||
#~ "\n"
|
||||
#~ "Here, we have two plans: Projects and Salesman. An invoice line must\n"
|
||||
#~ "be able to write analytic entries in the 2 plans: SubProj 1.1 and\n"
|
||||
#~ "Fabien. The amount can also be split. The following example is for\n"
|
||||
#~ "an invoice that touches the two subproject and assigned to one salesman:\n"
|
||||
#~ "\n"
|
||||
#~ "Plan1:\n"
|
||||
#~ " SubProject 1.1 : 50%\n"
|
||||
#~ " SubProject 1.2 : 50%\n"
|
||||
#~ "Plan2:\n"
|
||||
#~ " Eric: 100%\n"
|
||||
#~ "\n"
|
||||
#~ "So when this line of invoice will be confirmed, it will generate 3 analytic "
|
||||
#~ "lines,\n"
|
||||
#~ "for one account entry.\n"
|
||||
#~ "The analytic plan validates the minimum and maximum percentage at the time "
|
||||
#~ "of creation\n"
|
||||
#~ "of distribution models.\n"
|
||||
#~ " "
|
||||
#~ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "Este módulo permite usar varios planes analíticos, según el diario general.\n"
|
||||
#~ "============================================================================="
|
||||
#~ "======\n"
|
||||
#~ "\n"
|
||||
#~ "Se crean varias líneas analíticas cuando la factura o las entradas se "
|
||||
#~ "confirman.\n"
|
||||
#~ "\n"
|
||||
#~ "Por ejemplo, se puede definir la siguiente estructura analítica:\n"
|
||||
#~ " Proyectos\n"
|
||||
#~ " Proyecto 1\n"
|
||||
#~ " SubProyecto 1.1\n"
|
||||
#~ " SubProyecto 1.2\n"
|
||||
#~ " Proyecto 2\n"
|
||||
#~ "\n"
|
||||
#~ " Comercial\n"
|
||||
#~ " Eric\n"
|
||||
#~ " Fabien\n"
|
||||
#~ "\n"
|
||||
#~ "Como vemos, hay dos planes: Proyectos y Comercial.\n"
|
||||
#~ "Una lñinea de factura debe ser capaz de escribir entradas analíticas\n"
|
||||
#~ "en dos planes: SubProyecto 1.1 y Fabien. También se puede repartir la "
|
||||
#~ "cantidad.\n"
|
||||
#~ "\n"
|
||||
#~ "El siguiente ejemplo es para una factura que afecta a los dos subproyectos\n"
|
||||
#~ "y se asigna a uno de los comerciales:\n"
|
||||
#~ "\n"
|
||||
#~ "Plan1:\n"
|
||||
#~ " SubProyecto 1.1 : 50%\n"
|
||||
#~ " SubProyecto 1.2 : 50%\n"
|
||||
#~ "Plan2:\n"
|
||||
#~ " Eric: 100%\n"
|
||||
#~ "\n"
|
||||
#~ "Así, cuando se confirme la línea de factura, se generarán 3 líneas "
|
||||
#~ "analíticas,\n"
|
||||
#~ "para un asiento contable.\n"
|
||||
#~ "El plan analítico valida los porcentajes mínimo y máximo en el momento de "
|
||||
#~ "creación\n"
|
||||
#~ "de los modelos distribuidos.\n"
|
||||
#~ " "
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ "This module allows you to produce several products from one production "
|
||||
#~ "order.\n"
|
||||
#~ "============================================================================="
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "You can configure sub-products in the bill of material.\n"
|
||||
#~ "\n"
|
||||
#~ "Without this module:\n"
|
||||
#~ " A + B + C -> D\n"
|
||||
#~ "\n"
|
||||
#~ "With this module:\n"
|
||||
#~ " A + B + C -> D + E\n"
|
||||
#~ " "
|
||||
#~ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "Este módulo permite producir varios productos desde una única orden de "
|
||||
#~ "producción.\n"
|
||||
#~ "============================================================================="
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "Se pueden configurar subproductos en la lista de materiales.\n"
|
||||
#~ "\n"
|
||||
#~ "Sin este módulo:\n"
|
||||
#~ " A + B + C -> D\n"
|
||||
#~ "\n"
|
||||
#~ "Con este módulo:\n"
|
||||
#~ " A + B + C -> D + E\n"
|
||||
#~ " "
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:54+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
"Language: \n"
|
||||
|
||||
#. module: base
|
||||
|
@ -191,7 +191,7 @@ msgstr "Ventana destino"
|
|||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_rml:0
|
||||
msgid "Main Report File Path"
|
||||
msgstr ""
|
||||
msgstr "Ruta de archivo principal del informe"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
|
||||
|
@ -212,6 +212,15 @@ msgid ""
|
|||
"revenue\n"
|
||||
"reports."
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Genere facturas desde los gastos y partes de horas.\n"
|
||||
"========================================================\n"
|
||||
"\n"
|
||||
"Módulo para generar facturas basadas en los costes (recursos humanos, "
|
||||
"gastos, ...).\n"
|
||||
"\n"
|
||||
"Puede definir tarifas en la contabilidad analítica, realizando algún informe "
|
||||
"teórico de beneficios."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_sequence.py:134
|
||||
|
@ -263,7 +272,7 @@ msgstr "creado."
|
|||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_xsl:0
|
||||
msgid "XSL Path"
|
||||
msgstr ""
|
||||
msgstr "Ruta XSL"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_tr
|
||||
|
@ -289,7 +298,7 @@ msgstr "Inuktitut / ᐃᓄᒃᑎᑐᑦ"
|
|||
#. module: base
|
||||
#: model:res.groups,name:base.group_multi_currency
|
||||
msgid "Multi Currencies"
|
||||
msgstr ""
|
||||
msgstr "Multidivisa"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_cl
|
||||
|
@ -301,6 +310,12 @@ msgid ""
|
|||
"\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Plan de cuentas y localización de impuestos chilenos.\n"
|
||||
"==============================================\n"
|
||||
"Plan contable chileno e impuestos de acuerdo a disposiciones vigentes\n"
|
||||
"\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale
|
||||
|
@ -313,6 +328,7 @@ msgid ""
|
|||
"The internal user that is in charge of communicating with this contact if "
|
||||
"any."
|
||||
msgstr ""
|
||||
"El usuario interno encargado de comunicarse con este contacto si lo hubiese."
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:54+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -8,14 +8,15 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
|
||||
"PO-Revision-Date: 2012-09-07 01:11+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"PO-Revision-Date: 2013-07-25 03:08+0000\n"
|
||||
"Last-Translator: Federico Manuel Echeverri Choux - ( Vauxoo ) "
|
||||
"<echeverrifm@gmail.com>\n"
|
||||
"Language-Team: Spanish (Mexico) <es_MX@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:54+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -613,7 +614,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:res.country,name:0
|
||||
msgid "Country Name"
|
||||
msgstr ""
|
||||
msgstr "Nombre del país"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.co
|
||||
|
@ -14300,7 +14301,7 @@ msgstr ""
|
|||
#: field:res.partner,vat:0
|
||||
#, python-format
|
||||
msgid "TIN"
|
||||
msgstr ""
|
||||
msgstr "RFC"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.aw
|
||||
|
@ -14316,7 +14317,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.country,name:base.ar
|
||||
msgid "Argentina"
|
||||
msgstr ""
|
||||
msgstr "Argentina"
|
||||
|
||||
#. module: base
|
||||
#: field:res.groups,full_name:0
|
||||
|
@ -14357,7 +14358,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_report_designer
|
||||
msgid "Advanced Reporting"
|
||||
msgstr ""
|
||||
msgstr "Informes avanzados"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_purchase
|
||||
|
@ -14405,7 +14406,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_fr
|
||||
msgid "France - Accounting"
|
||||
msgstr ""
|
||||
msgstr "Francia - Contabilidad"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.todo:0
|
||||
|
@ -14549,7 +14550,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Czech / Čeština"
|
||||
msgstr ""
|
||||
msgstr "Checo / Čeština"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_generic_modules
|
||||
|
@ -14608,7 +14609,7 @@ msgstr ""
|
|||
#: model:ir.module.category,name:base.module_category_hidden
|
||||
#: view:res.users:0
|
||||
msgid "Technical Settings"
|
||||
msgstr ""
|
||||
msgstr "Configuración técnica"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,description:base.module_category_accounting_and_finance
|
||||
|
@ -14616,6 +14617,8 @@ msgid ""
|
|||
"Helps you handle your accounting needs, if you are not an accountant, we "
|
||||
"suggest you to install only the Invoicing."
|
||||
msgstr ""
|
||||
"Le ayuda a manejar sus necesidades contables. Si no es un contable, le "
|
||||
"recomendamos que sólo instale el módulo 'invoicing'."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_plugin_thunderbird
|
||||
|
@ -14625,7 +14628,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_event
|
||||
msgid "Trainings, Conferences, Meetings, Exhibitions, Registrations"
|
||||
msgstr ""
|
||||
msgstr "Formaciones, conferencias, reuniones, exhibiciones, inscripciones"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_country
|
||||
|
@ -14688,7 +14691,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_nl
|
||||
msgid "Netherlands - Accounting"
|
||||
msgstr ""
|
||||
msgstr "Holanda - Contabilidad"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.gs
|
||||
|
@ -14713,6 +14716,11 @@ msgid ""
|
|||
"1,06,500;[1,2,-1] will represent it to be 106,50,0;[3] will represent it as "
|
||||
"106,500. Provided ',' as the thousand separator in each case."
|
||||
msgstr ""
|
||||
"El formato de separación debería ser como [,n] dónde 0 < n, empezando por "
|
||||
"el dígito unidad. -1 terminará la separación. Por ej. [3,2,-1] representará "
|
||||
"106500 como 1,06,500; [1,2,-1] lo representará como 106,50,0; [3] lo "
|
||||
"representará como 106,500. Siempre que ',' sea el separador de mil en cada "
|
||||
"caso."
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,auto_install:0
|
||||
|
@ -14732,6 +14740,12 @@ msgid ""
|
|||
"taxes\n"
|
||||
"and the Lempira currency."
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Éste es el módulo base para gestionar el plan de cuentas para Honduras.\n"
|
||||
"====================================================================\n"
|
||||
" \n"
|
||||
"Agrega una nomenclatura contable para Honduras. También incluye impuestos y "
|
||||
"la moneda Lempira."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.jp
|
||||
|
@ -14798,12 +14812,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_ca
|
||||
msgid "Canada - Accounting"
|
||||
msgstr ""
|
||||
msgstr "Canada - Contabilidad"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_co
|
||||
msgid "Colombian - Accounting"
|
||||
msgstr ""
|
||||
msgstr "Contabilidad colombiana"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_voucher
|
||||
|
@ -14870,7 +14884,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_ve
|
||||
msgid "Venezuela - Accounting"
|
||||
msgstr ""
|
||||
msgstr "Venezuela - Contabilidad"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.cl
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -9,8 +9,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
"X-Poedit-Country: IRAN, ISLAMIC REPUBLIC OF\n"
|
||||
"X-Poedit-Language: Persian\n"
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:54+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -8891,7 +8891,7 @@ msgstr "Sur plusieurs documents"
|
|||
#: view:res.users:0
|
||||
#: view:wizard.ir.model.menu.create:0
|
||||
msgid "or"
|
||||
msgstr ""
|
||||
msgstr "ou"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_accountant
|
||||
|
@ -9591,7 +9591,7 @@ msgstr ""
|
|||
" </p><p>\n"
|
||||
" OpenERP vous aide à suivre toutes les activités avec un "
|
||||
"client : \n"
|
||||
" les discussions, l'historiques des opportunités, \n"
|
||||
" les discussions, l'historique des opportunités, \n"
|
||||
" les documents, etc.\n"
|
||||
" </p>\n"
|
||||
" "
|
||||
|
@ -10040,7 +10040,7 @@ msgstr "Recherche toujours possible"
|
|||
#. module: base
|
||||
#: help:res.country.state,code:0
|
||||
msgid "The state code in max. three chars."
|
||||
msgstr ""
|
||||
msgstr "Code de l'état, avec au plus trois caractères."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.hk
|
||||
|
@ -10976,7 +10976,7 @@ msgstr "Mali"
|
|||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_project_config_project
|
||||
msgid "Stages"
|
||||
msgstr ""
|
||||
msgstr "Étapes"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -12836,7 +12836,7 @@ msgstr ""
|
|||
#: model:ir.actions.act_window,name:base.action_inventory_form
|
||||
#: model:ir.ui.menu,name:base.menu_action_inventory_form
|
||||
msgid "Default Company per Object"
|
||||
msgstr ""
|
||||
msgstr "Société par défaut par objet"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_hello
|
||||
|
@ -13383,7 +13383,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_fields_converter
|
||||
msgid "ir.fields.converter"
|
||||
msgstr ""
|
||||
msgstr "ir.fields.converter"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_partner.py:439
|
||||
|
@ -13411,7 +13411,7 @@ msgstr "Annuler l'installation"
|
|||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_model_relation
|
||||
msgid "ir.model.relation"
|
||||
msgstr ""
|
||||
msgstr "ir.model.relation"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_check_writing
|
||||
|
@ -14294,7 +14294,7 @@ msgstr "Banque"
|
|||
#: model:ir.module.category,name:base.module_category_point_of_sale
|
||||
#: model:ir.module.module,shortdesc:base.module_point_of_sale
|
||||
msgid "Point of Sale"
|
||||
msgstr ""
|
||||
msgstr "Point de vente"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_mail
|
||||
|
@ -15202,7 +15202,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Fax:"
|
||||
msgstr ""
|
||||
msgstr "Fax :"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.ui.view,type:0
|
||||
|
@ -15968,7 +15968,7 @@ msgstr "Modules à mettre à jour"
|
|||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_custom_multicompany
|
||||
msgid "Multi-Companies"
|
||||
msgstr ""
|
||||
msgstr "Multi-sociétés"
|
||||
|
||||
#. module: base
|
||||
#: field:workflow,osv:0
|
||||
|
@ -16996,7 +16996,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.attachment,file_size:0
|
||||
msgid "File Size"
|
||||
msgstr ""
|
||||
msgstr "Taille du fichier"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.sequence,prefix:0
|
||||
|
@ -17011,7 +17011,7 @@ msgstr "Seychelles"
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_4
|
||||
msgid "Gold"
|
||||
msgstr ""
|
||||
msgstr "Or"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_company.py:173
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -29,12 +29,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
msgid "Saint Helena"
|
||||
msgstr ""
|
||||
msgstr "સંત હેલેના"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.report.xml:0
|
||||
msgid "Other Configuration"
|
||||
msgstr "બીજા કન્ફિગ્યુરેશન"
|
||||
msgstr "અન્ય રેખાંકન"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.property,type:0
|
||||
|
@ -232,7 +232,7 @@ msgstr "સ્પાર્સ ફિલ્ડ \"%s\" નુ નામ બદલ
|
|||
#. module: base
|
||||
#: model:res.country,name:base.sz
|
||||
msgid "Swaziland"
|
||||
msgstr "સ્વાઝિલેન્ડ"
|
||||
msgstr "સ્વાઝીલેન્ડ"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:4485
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
#. module: base
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -9,8 +9,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -42,7 +42,7 @@ msgstr "Konfigurasi Lainnya"
|
|||
#. module: base
|
||||
#: selection:ir.property,type:0
|
||||
msgid "DateTime"
|
||||
msgstr ""
|
||||
msgstr "DateTime"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/fields.py:652
|
||||
|
@ -51,6 +51,8 @@ msgid ""
|
|||
"The second argument of the many2many field %s must be a SQL table !You used "
|
||||
"%s, which is not a valid SQL table name."
|
||||
msgstr ""
|
||||
"Argumen kedua dari field many2many %s harus merupakan tabel SQL ! Anda "
|
||||
"menggunakan %s, yang mana bukan table SQL."
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.view,arch:0
|
||||
|
@ -66,7 +68,7 @@ msgstr "Kontrol Penawaran, Pesanan Penjualan, Pengiriman & Faktur"
|
|||
#. module: base
|
||||
#: selection:ir.sequence,implementation:0
|
||||
msgid "No gap"
|
||||
msgstr ""
|
||||
msgstr "Tidak ada gap"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -76,7 +78,7 @@ msgstr "Bahasa Hungaria"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Spanish (PY) / Español (PY)"
|
||||
msgstr ""
|
||||
msgstr "Bahasa Spanyol"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,description:base.module_category_project_management
|
||||
|
@ -84,27 +86,30 @@ msgid ""
|
|||
"Helps you manage your projects and tasks by tracking them, generating "
|
||||
"plannings, etc..."
|
||||
msgstr ""
|
||||
"Memungkinkan Anda untuk mengelola proyek dan tugas dengan cara melacaknya, "
|
||||
"menghasilkan perencanaan dan lain sebagainya ..."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_point_of_sale
|
||||
msgid "Touchscreen Interface for Shops"
|
||||
msgstr ""
|
||||
msgstr "Antarmuka layar sentuh untuk digunakan di toko"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
|
||||
msgid "Indian Payroll"
|
||||
msgstr ""
|
||||
msgstr "Payroll untuk India"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.cron,model:0
|
||||
msgid ""
|
||||
"Model name on which the method to be called is located, e.g. 'res.partner'."
|
||||
msgstr ""
|
||||
"Sebutkan nama model yang mana metode ini akan dipanggil, misal 'res.partner'."
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
msgid "Created Views"
|
||||
msgstr "View Tercipta"
|
||||
msgstr "View yang diciptakan"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_product_manufacturer
|
||||
|
@ -121,11 +126,24 @@ msgid ""
|
|||
" * Product Attributes\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Modul ini berfungsi untuk menambahkan nama produsen beserta atributnya pada "
|
||||
"formulir produk.\n"
|
||||
"============================================================================="
|
||||
"==\n"
|
||||
"\n"
|
||||
"Anda dapat mengisi data-data berikut pada produk:\n"
|
||||
"------------------------------------------------\n"
|
||||
"* Nama produsen\n"
|
||||
"* Nama produk produsen\n"
|
||||
"* Code produk produsen\n"
|
||||
"* Atribut produk\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.client,params:0
|
||||
msgid "Supplementary arguments"
|
||||
msgstr ""
|
||||
msgstr "Argumen tambahan"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_google_base_account
|
||||
|
@ -134,11 +152,14 @@ msgid ""
|
|||
"The module adds google user in res user.\n"
|
||||
"========================================\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Modul ini akan menambahkan google user dalam obyek res.user\n"
|
||||
"====================================================\n"
|
||||
|
||||
#. module: base
|
||||
#: help:res.partner,employee:0
|
||||
msgid "Check this box if this contact is an Employee."
|
||||
msgstr ""
|
||||
msgstr "Centang kotak ini jika kontak adalah pegawai"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model.fields,domain:0
|
||||
|
@ -147,6 +168,9 @@ msgid ""
|
|||
"specified as a Python expression defining a list of triplets. For example: "
|
||||
"[('color','=','red')]"
|
||||
msgstr ""
|
||||
"Sebuah domain (opsional) untuk membatasi nilai untuk field yang terelasi, "
|
||||
"dimana merupakan ekspresi Python yang mendefinisikan daftar triplet. Sebagai "
|
||||
"contoh: [('color', '=', 'merah')]"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,ref:0
|
||||
|
@ -156,7 +180,7 @@ msgstr "Referensi"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_be_invoice_bba
|
||||
msgid "Belgium - Structured Communication"
|
||||
msgstr ""
|
||||
msgstr "Belgia - Komunikasi terstruktur"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,target:0
|
||||
|
@ -166,12 +190,12 @@ msgstr "Jendela Sasaran"
|
|||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_rml:0
|
||||
msgid "Main Report File Path"
|
||||
msgstr ""
|
||||
msgstr "Path (lokasi) file laporan"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
|
||||
msgid "Sales Analytic Distribution"
|
||||
msgstr ""
|
||||
msgstr "Untuk mendistribusikan data analisis pada proses Penjualan"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_hr_timesheet_invoice
|
||||
|
@ -203,12 +227,14 @@ msgid ""
|
|||
"Properties of base fields cannot be altered in this manner! Please modify "
|
||||
"them through Python code, preferably through a custom addon!"
|
||||
msgstr ""
|
||||
"Sifat field basis tidak dapat diubah dengan cara ini! Silahkan "
|
||||
"memodifikasinya melalui kode Python, sebaiknya dibuat modul terpisah."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/osv.py:151
|
||||
#, python-format
|
||||
msgid "Constraint Error"
|
||||
msgstr ""
|
||||
msgstr "Kesalahan"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_ui_view_custom
|
||||
|
@ -219,7 +245,7 @@ msgstr "ir.ui.view.custom"
|
|||
#: code:addons/base/ir/ir_model.py:374
|
||||
#, python-format
|
||||
msgid "Renaming sparse field \"%s\" is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Mengganti nama field \"%s\" tidak diperbolehkan"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sz
|
||||
|
@ -235,12 +261,12 @@ msgstr "dibuat"
|
|||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_xsl:0
|
||||
msgid "XSL Path"
|
||||
msgstr ""
|
||||
msgstr "Lokasi file XSL"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_tr
|
||||
msgid "Turkey - Accounting"
|
||||
msgstr ""
|
||||
msgstr "Turki - Akunting"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.sequence,number_increment:0
|
||||
|
@ -256,12 +282,12 @@ msgstr "Struktur Perusahaan"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Inuktitut / ᐃᓄᒃᑎᑐᑦ"
|
||||
msgstr ""
|
||||
msgstr "Inuktitut / ᐃᓄᒃᑎᑐᑦ"
|
||||
|
||||
#. module: base
|
||||
#: model:res.groups,name:base.group_multi_currency
|
||||
msgid "Multi Currencies"
|
||||
msgstr ""
|
||||
msgstr "Multi Mata Uang"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_cl
|
||||
|
@ -277,7 +303,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale
|
||||
msgid "Sales Management"
|
||||
msgstr ""
|
||||
msgstr "Manajemen Penjualan"
|
||||
|
||||
#. module: base
|
||||
#: help:res.partner,user_id:0
|
||||
|
@ -285,6 +311,7 @@ msgid ""
|
|||
"The internal user that is in charge of communicating with this contact if "
|
||||
"any."
|
||||
msgstr ""
|
||||
"Pengguna/user yang ditugaskan berhubungan dengan kontak ini, jika ada."
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
|
@ -299,7 +326,7 @@ msgstr "Jumlah Modul"
|
|||
#. module: base
|
||||
#: help:multi_company.default,company_dest_id:0
|
||||
msgid "Company to store the current record"
|
||||
msgstr ""
|
||||
msgstr "Perusahaan untuk menyimpan data ini"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.bank.type.field,size:0
|
||||
|
@ -312,6 +339,8 @@ msgid ""
|
|||
"Database ID of record to open in form view, when ``view_mode`` is set to "
|
||||
"'form' only"
|
||||
msgstr ""
|
||||
"Database ID dari data yang akan dibuka dalam tampilan formulir (form view), "
|
||||
"dimana \"view_mode\" diberi nilai \"form\"."
|
||||
|
||||
#. module: base
|
||||
#: help:ir.values,key2:0
|
||||
|
@ -327,7 +356,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: sql_constraint:res.lang:0
|
||||
msgid "The name of the language must be unique !"
|
||||
msgstr ""
|
||||
msgstr "Nama bahasa harus unik"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.request,state:0
|
||||
|
@ -355,7 +384,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_customer_relationship_management
|
||||
msgid "Customer Relationship Management"
|
||||
msgstr ""
|
||||
msgstr "Manajemen Hubungan Pelanggan"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_delivery
|
||||
|
@ -377,40 +406,42 @@ msgid ""
|
|||
"There is already a shared filter set as default for %(model)s, delete or "
|
||||
"change it before setting a new default"
|
||||
msgstr ""
|
||||
"Sudah ada filter yang telah ditetapkan untuk %(model)s, silakan hapus atau "
|
||||
"rubah dulu sebelum menetapkan nilai baru."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:2649
|
||||
#, python-format
|
||||
msgid "Invalid group_by"
|
||||
msgstr ""
|
||||
msgstr "group_by salah"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.category,child_ids:0
|
||||
msgid "Child Applications"
|
||||
msgstr ""
|
||||
msgstr "Aplikasi Anak"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,credit_limit:0
|
||||
msgid "Credit Limit"
|
||||
msgstr ""
|
||||
msgstr "Batas Kredit"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.constraint,date_update:0
|
||||
#: field:ir.model.data,date_update:0
|
||||
#: field:ir.model.relation,date_update:0
|
||||
msgid "Update Date"
|
||||
msgstr "Tanggal Pembaharuan"
|
||||
msgstr "Pembaharuan Tanggal"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_base_action_rule
|
||||
msgid "Automated Action Rules"
|
||||
msgstr ""
|
||||
msgstr "Aturan Aksi Otomatis"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
#: field:ir.attachment,create_uid:0
|
||||
msgid "Owner"
|
||||
msgstr ""
|
||||
msgstr "Pemilik"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.act_window:0
|
||||
|
@ -420,7 +451,7 @@ msgstr "Sumber Obyek"
|
|||
#. module: base
|
||||
#: model:res.partner.bank.type,format_layout:base.bank_normal
|
||||
msgid "%(bank_name)s: %(acc_number)s"
|
||||
msgstr ""
|
||||
msgstr "%(bank_name)s: %(acc_number)s"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.todo:0
|
||||
|
@ -445,6 +476,8 @@ msgid ""
|
|||
"Invalid date/time format directive specified. Please refer to the list of "
|
||||
"allowed directives, displayed when you edit a language."
|
||||
msgstr ""
|
||||
"Penetapan format data/time salah. Silakan contoh yang ditampilkan ketika "
|
||||
"mengedit bahasa."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:4153
|
||||
|
@ -452,7 +485,7 @@ msgstr ""
|
|||
msgid ""
|
||||
"One of the records you are trying to modify has already been deleted "
|
||||
"(Document type: %s)."
|
||||
msgstr ""
|
||||
msgstr "Data yang akan Anda modifikasi sudah dihapus (Jenis Dokumen: %s)."
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,views:0
|
||||
|
@ -466,12 +499,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.model.relation,name:0
|
||||
msgid "Relation Name"
|
||||
msgstr ""
|
||||
msgstr "Nama Relasi"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Create Access Right"
|
||||
msgstr ""
|
||||
msgstr "Membuat Hak Akses"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tv
|
||||
|
@ -481,7 +514,7 @@ msgstr "Tuvalu"
|
|||
#. module: base
|
||||
#: field:ir.actions.configuration.wizard,note:0
|
||||
msgid "Next Wizard"
|
||||
msgstr ""
|
||||
msgstr "Wizard berikutnya"
|
||||
|
||||
#. module: base
|
||||
#: field:res.lang,date_format:0
|
||||
|
@ -491,7 +524,7 @@ msgstr "Format Tanggal"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_base_report_designer
|
||||
msgid "OpenOffice Report Designer"
|
||||
msgstr ""
|
||||
msgstr "Untuk mendesign Laporan dengan OpenOffice"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.an
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -2052,7 +2052,7 @@ msgstr "Turkmenistan"
|
|||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "7. %H:%M:%S ==> 18:25:20"
|
||||
msgstr ""
|
||||
msgstr "7. %H:%M:%S ==> 18:25:20"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
|
@ -2579,7 +2579,7 @@ msgstr "Dodatkowe"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.st
|
||||
msgid "Saint Tome (Sao Tome) and Principe"
|
||||
msgstr ""
|
||||
msgstr "Wyspy Świętego Tomasza i Książęca"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.partner,type:0
|
||||
|
@ -4261,7 +4261,7 @@ msgstr "Wymagana grupa"
|
|||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "6. %d, %m ==> 05, 12"
|
||||
msgstr ""
|
||||
msgstr "6. %d, %m ==> 05, 12"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_it
|
||||
|
@ -5146,7 +5146,7 @@ msgstr "Ścieżka XML"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.bj
|
||||
msgid "Benin"
|
||||
msgstr ""
|
||||
msgstr "Benin"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_res_partner_bank_type_form
|
||||
|
@ -5340,7 +5340,7 @@ msgstr "Syria"
|
|||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "======================================================"
|
||||
msgstr ""
|
||||
msgstr "======================================================"
|
||||
|
||||
#. module: base
|
||||
#: sql_constraint:ir.model:0
|
||||
|
@ -5453,6 +5453,7 @@ msgstr "Nazwa posiadacza konta"
|
|||
#, python-format
|
||||
msgid "Cannot rename column to %s, because that column already exists!"
|
||||
msgstr ""
|
||||
"Nie można zmienić nazwy kolumny na %s, ponieważ ta kolumna już istnieje!"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
|
@ -5578,7 +5579,7 @@ msgstr "Wenezuela"
|
|||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "9. %j ==> 340"
|
||||
msgstr ""
|
||||
msgstr "9. %j ==> 340"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.zm
|
||||
|
@ -5943,7 +5944,7 @@ msgstr "Pogrupuj wg"
|
|||
#. module: base
|
||||
#: view:res.config.installer:0
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
msgstr "tytuł"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_fields.py:146
|
||||
|
@ -5969,7 +5970,7 @@ msgstr "Tłumaczenie"
|
|||
#. module: base
|
||||
#: selection:res.request,state:0
|
||||
msgid "closed"
|
||||
msgstr ""
|
||||
msgstr "zamknięte"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.export,state:0
|
||||
|
@ -12108,7 +12109,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: selection:ir.translation,type:0
|
||||
msgid "XSL"
|
||||
msgstr ""
|
||||
msgstr "XSL"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:85
|
||||
|
@ -15906,10 +15907,6 @@ msgstr ""
|
|||
#~ msgid "On delete"
|
||||
#~ msgstr "Przy usuwaniu"
|
||||
|
||||
#, python-format
|
||||
#~ msgid "Not Implemented"
|
||||
#~ msgstr "Nie zaimplementowane"
|
||||
|
||||
#~ msgid "Textile Suppliers"
|
||||
#~ msgstr "Dostawca tekstyliów"
|
||||
|
||||
|
@ -17034,3 +17031,58 @@ msgstr ""
|
|||
#~ "======================================\n"
|
||||
#~ "\n"
|
||||
#~ "Moduł umożliwia planowanie wsteczne zarządzające twoimi wydarzeniami.\n"
|
||||
|
||||
#, python-format
|
||||
#~ msgid "Not Implemented"
|
||||
#~ msgstr "Niezaimplementowane"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "OpenERP translations (core, modules, clients) are managed through "
|
||||
#~ "Launchpad.net, our open source project management facility. We use their "
|
||||
#~ "online interface to synchronize all translations efforts."
|
||||
#~ msgstr ""
|
||||
#~ "Tłumaczenia OpenERP (jądro, moduły, klient) są zarządzane poprzez "
|
||||
#~ "Launchpad.net. Używamy ich interfejsu online by synchronizować wszelkie "
|
||||
#~ "tłumaczenia."
|
||||
|
||||
#~ msgid ","
|
||||
#~ msgstr ","
|
||||
|
||||
#~ msgid "S. Georgia & S. Sandwich Isls."
|
||||
#~ msgstr "Georgia Południowa i Sandwich Południowy"
|
||||
|
||||
#~ msgid "-"
|
||||
#~ msgstr "-"
|
||||
|
||||
#~ msgid "https://help.launchpad.net/Translations"
|
||||
#~ msgstr "https://help.launchpad.net/Translations"
|
||||
|
||||
#~ msgid "Portugese (BR) / Português (BR)"
|
||||
#~ msgstr "Portugalski (BR) / Português (BR)"
|
||||
|
||||
#~ msgid "Facebook"
|
||||
#~ msgstr "Facebook"
|
||||
|
||||
#~ msgid "Openstuff.net"
|
||||
#~ msgstr "Openstuff.net"
|
||||
|
||||
#~ msgid "Portugese / Português"
|
||||
#~ msgstr "Portugalski / Português"
|
||||
|
||||
#~ msgid "M."
|
||||
#~ msgstr "M."
|
||||
|
||||
#~ msgid "XML ID"
|
||||
#~ msgstr "XML ID"
|
||||
|
||||
#~ msgid "XML Id"
|
||||
#~ msgstr "XML Id"
|
||||
|
||||
#~ msgid "FYROM"
|
||||
#~ msgstr "FYROM"
|
||||
|
||||
#~ msgid "Serial Key"
|
||||
#~ msgstr "Klucz seryjny"
|
||||
|
||||
#~ msgid "Widget Wizard"
|
||||
#~ msgstr "Kreator widżetów"
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:42+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:44+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:53+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -2465,6 +2465,8 @@ msgstr ""
|
|||
msgid ""
|
||||
"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'"
|
||||
msgstr ""
|
||||
"'%%(field)s' Alanında %(field_type)s '%(value)s' için eşleşen kayıt "
|
||||
"bulunamadı"
|
||||
|
||||
#. module: base
|
||||
#: field:change.password.user,new_passwd:0
|
||||
|
@ -15574,6 +15576,8 @@ msgid ""
|
|||
"Tax Identification Number. Check the box if this contact is subjected to "
|
||||
"taxes. Used by the some of the legal statements."
|
||||
msgstr ""
|
||||
"Vergi Kimlik Numarası. Bu kişi vergiye tabiyse kutuyu işaretleyin. Bazı "
|
||||
"yasal belgelerde kullanılır."
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.bank,partner_id:0
|
||||
|
@ -15642,6 +15646,8 @@ msgid ""
|
|||
"Manage relations with prospects and customers using leads, opportunities, "
|
||||
"requests or issues."
|
||||
msgstr ""
|
||||
"Adayları, fırsatları, istekleri ya da sorunları kullanarak potansiyeller ve "
|
||||
"müşterilerle ilişkileri yönetin."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_project
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:54+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-07-06 05:52+0000\n"
|
||||
"X-Generator: Launchpad (build 16696)\n"
|
||||
"X-Launchpad-Export-Date: 2013-08-03 05:54+0000\n"
|
||||
"X-Generator: Launchpad (build 16718)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
|
|
@ -19,20 +19,20 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from functools import partial
|
||||
import logging
|
||||
import operator
|
||||
import os
|
||||
import re
|
||||
from socket import gethostname
|
||||
import time
|
||||
|
||||
import openerp
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import tools
|
||||
from openerp import workflow
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.osv.orm import browse_record
|
||||
import openerp.report.interface
|
||||
from openerp.report.report_sxw import report_sxw, report_rml
|
||||
from openerp.tools.config import config
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
import openerp.workflow
|
||||
|
@ -429,121 +429,177 @@ class server_object_lines(osv.osv):
|
|||
_name = 'ir.server.object.lines'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_columns = {
|
||||
'server_id': fields.many2one('ir.actions.server', 'Object Mapping'),
|
||||
'col1': fields.many2one('ir.model.fields', 'Destination', required=True),
|
||||
'server_id': fields.many2one('ir.actions.server', 'Related Server Action'),
|
||||
'col1': fields.many2one('ir.model.fields', 'Field', required=True),
|
||||
'value': fields.text('Value', required=True, help="Expression containing a value specification. \n"
|
||||
"When Formula type is selected, this field may be a Python expression "
|
||||
" that can use the same values as for the condition field on the server action.\n"
|
||||
"If Value type is selected, the value will be used directly without evaluation."),
|
||||
'type': fields.selection([
|
||||
('value','Value'),
|
||||
('equation','Formula')
|
||||
], 'Type', required=True, size=32, change_default=True),
|
||||
('value', 'Value'),
|
||||
('equation', 'Python expression')
|
||||
], 'Evaluation Type', required=True, change_default=True),
|
||||
}
|
||||
_defaults = {
|
||||
'type': 'equation',
|
||||
'type': 'value',
|
||||
}
|
||||
server_object_lines()
|
||||
|
||||
|
||||
##
|
||||
# Actions that are run on the server side
|
||||
#
|
||||
class actions_server(osv.osv):
|
||||
""" Server actions model. Server action work on a base model and offer various
|
||||
type of actions that can be executed automatically, for example using base
|
||||
action rules, of manually, by adding the action in the 'More' contextual
|
||||
menu.
|
||||
|
||||
def _select_signals(self, cr, uid, context=None):
|
||||
cr.execute("""SELECT distinct w.osv, t.signal FROM wkf w, wkf_activity a, wkf_transition t
|
||||
WHERE w.id = a.wkf_id AND
|
||||
(t.act_from = a.id OR t.act_to = a.id) AND
|
||||
t.signal IS NOT NULL""")
|
||||
result = cr.fetchall() or []
|
||||
res = []
|
||||
for rs in result:
|
||||
if rs[0] is not None and rs[1] is not None:
|
||||
line = rs[1], "%s - (%s)" % (rs[1], rs[0])
|
||||
res.append(line)
|
||||
return res
|
||||
Since OpenERP 8.0 a button 'Create Menu Action' button is available on the
|
||||
action form view. It creates an entry in the More menu of the base model.
|
||||
This allows to create server actions and run them in mass mode easily through
|
||||
the interface.
|
||||
|
||||
def _select_objects(self, cr, uid, context=None):
|
||||
model_pool = self.pool.get('ir.model')
|
||||
ids = model_pool.search(cr, uid, [('name','not ilike','.')])
|
||||
res = model_pool.read(cr, uid, ids, ['model', 'name'])
|
||||
return [(r['model'], r['name']) for r in res] + [('','')]
|
||||
|
||||
def change_object(self, cr, uid, ids, copy_object, state, context=None):
|
||||
if state == 'object_copy' and copy_object:
|
||||
if context is None:
|
||||
context = {}
|
||||
model_pool = self.pool.get('ir.model')
|
||||
model = copy_object.split(',')[0]
|
||||
mid = model_pool.search(cr, uid, [('model','=',model)])
|
||||
return {
|
||||
'value': {'srcmodel_id': mid[0]},
|
||||
'context': context
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
The available actions are :
|
||||
|
||||
- 'Execute Python Code': a block of python code that will be executed
|
||||
- 'Trigger a Workflow Signal': send a signal to a workflow
|
||||
- 'Run a Client Action': choose a client action to launch
|
||||
- 'Create or Copy a new Record': create a new record with new values, or
|
||||
copy an existing record in your database
|
||||
- 'Write on a Record': update the values of a record
|
||||
- 'Execute several actions': define an action that triggers several other
|
||||
server actions
|
||||
"""
|
||||
_name = 'ir.actions.server'
|
||||
_table = 'ir_act_server'
|
||||
_inherit = 'ir.actions.actions'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'sequence,name'
|
||||
|
||||
def _select_objects(self, cr, uid, context=None):
|
||||
model_pool = self.pool.get('ir.model')
|
||||
ids = model_pool.search(cr, uid, [('name', 'not ilike', '.')])
|
||||
res = model_pool.read(cr, uid, ids, ['model', 'name'])
|
||||
return [(r['model'], r['name']) for r in res] + [('', '')]
|
||||
|
||||
def _get_states(self, cr, uid, context=None):
|
||||
""" Override me in order to add new states in the server action. Please
|
||||
note that the added key length should not be higher than already-existing
|
||||
ones. """
|
||||
return [('code', 'Execute Python Code'),
|
||||
('trigger', 'Trigger a Workflow Signal'),
|
||||
('client_action', 'Run a Client Action'),
|
||||
('object_create', 'Create or Copy a new Record'),
|
||||
('object_write', 'Write on a Record'),
|
||||
('multi', 'Execute several actions')]
|
||||
|
||||
def _get_states_wrapper(self, cr, uid, context=None):
|
||||
return self._get_states(cr, uid, context)
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Action Name', required=True, size=64, translate=True),
|
||||
'condition' : fields.char('Condition', size=256, required=True,
|
||||
help="Condition that is tested before the action is executed, "
|
||||
"and prevent execution if it is not verified.\n"
|
||||
"Example: object.list_price > 5000\n"
|
||||
"It is a Python expression that can use the following values:\n"
|
||||
" - self: ORM model of the record on which the action is triggered\n"
|
||||
" - object or obj: browse_record of the record on which the action is triggered\n"
|
||||
" - pool: ORM model pool (i.e. self.pool)\n"
|
||||
" - time: Python time module\n"
|
||||
" - cr: database cursor\n"
|
||||
" - uid: current user id\n"
|
||||
" - context: current context"),
|
||||
'state': fields.selection([
|
||||
('client_action','Client Action'),
|
||||
('dummy','Dummy'),
|
||||
('loop','Iteration'),
|
||||
('code','Python Code'),
|
||||
('trigger','Trigger'),
|
||||
('email','Email'),
|
||||
('sms','SMS'),
|
||||
('object_create','Create Object'),
|
||||
('object_copy','Copy Object'),
|
||||
('object_write','Write Object'),
|
||||
('other','Multi Actions'),
|
||||
], 'Action Type', required=True, size=32, help="Type of the Action that is to be executed"),
|
||||
'code':fields.text('Python Code', help="Python code to be executed if condition is met.\n"
|
||||
"It is a Python block that can use the same values as for the condition field"),
|
||||
'sequence': fields.integer('Sequence', help="Important when you deal with multiple actions, the execution order will be decided based on this, low number is higher priority."),
|
||||
'model_id': fields.many2one('ir.model', 'Object', required=True, help="Select the object on which the action will work (read, write, create).", ondelete='cascade'),
|
||||
'action_id': fields.many2one('ir.actions.actions', 'Client Action', help="Select the Action Window, Report, Wizard to be executed."),
|
||||
'trigger_name': fields.selection(_select_signals, string='Trigger Signal', size=128, help="The workflow signal to trigger"),
|
||||
'wkf_model_id': fields.many2one('ir.model', 'Target Object', help="The object that should receive the workflow signal (must have an associated workflow)"),
|
||||
'trigger_obj_id': fields.many2one('ir.model.fields','Relation Field', help="The field on the current object that links to the target object record (must be a many2one, or an integer field with the record ID)"),
|
||||
'email': fields.char('Email Address', size=512, help="Expression that returns the email address to send to. Can be based on the same values as for the condition field.\n"
|
||||
"Example: object.invoice_address_id.email, or 'me@example.com'"),
|
||||
'subject': fields.char('Subject', size=1024, translate=True, help="Email subject, may contain expressions enclosed in double brackets based on the same values as those "
|
||||
"available in the condition field, e.g. `Hello [[ object.partner_id.name ]]`"),
|
||||
'message': fields.text('Message', translate=True, help="Email contents, may contain expressions enclosed in double brackets based on the same values as those "
|
||||
"available in the condition field, e.g. `Dear [[ object.partner_id.name ]]`"),
|
||||
'mobile': fields.char('Mobile No', size=512, help="Provides fields that be used to fetch the mobile number, e.g. you select the invoice, then `object.invoice_address_id.mobile` is the field which gives the correct mobile number"),
|
||||
'sms': fields.char('SMS', size=160, translate=True),
|
||||
'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id', 'Other Actions'),
|
||||
'condition': fields.char('Condition',
|
||||
help="Condition verified before executing the server action. If it "
|
||||
"is not verified, the action will not be executed. The condition is "
|
||||
"a Python expression, like 'object.list_price > 5000'. A void "
|
||||
"condition is considered as always True. Help about python expression "
|
||||
"is given in the help tab."),
|
||||
'state': fields.selection(_get_states_wrapper, 'Action To Do', required=True,
|
||||
help="Type of server action. The following values are available:\n"
|
||||
"- 'Execute Python Code': a block of python code that will be executed\n"
|
||||
"- 'Trigger a Workflow Signal': send a signal to a workflow\n"
|
||||
"- 'Run a Client Action': choose a client action to launch\n"
|
||||
"- 'Create or Copy a new Record': create a new record with new values, or copy an existing record in your database\n"
|
||||
"- 'Write on a Record': update the values of a record\n"
|
||||
"- 'Execute several actions': define an action that triggers several other server actions\n"
|
||||
"- 'Send Email': automatically send an email (available in email_template)"),
|
||||
'usage': fields.char('Action Usage', size=32),
|
||||
'type': fields.char('Action Type', size=32, required=True),
|
||||
'srcmodel_id': fields.many2one('ir.model', 'Model', help="Object in which you want to create / write the object. If it is empty then refer to the Object field."),
|
||||
'fields_lines': fields.one2many('ir.server.object.lines', 'server_id', 'Field Mappings.'),
|
||||
'record_id':fields.many2one('ir.model.fields', 'Create Id', help="Provide the field name where the record id is stored after the create operations. If it is empty, you can not track the new record."),
|
||||
'write_id':fields.char('Write Id', size=256, help="Provide the field name that the record id refers to for the write operation. If it is empty it will refer to the active id of the object."),
|
||||
'loop_action':fields.many2one('ir.actions.server', 'Loop Action', help="Select the action that will be executed. Loop action will not be avaliable inside loop."),
|
||||
'expression':fields.char('Loop Expression', size=512, help="Enter the field/expression that will return the list. E.g. select the sale order in Object, and you can have loop on the sales order line. Expression = `object.order_line`."),
|
||||
'copy_object': fields.reference('Copy Of', selection=_select_objects, size=256),
|
||||
# Generic
|
||||
'sequence': fields.integer('Sequence',
|
||||
help="When dealing with multiple actions, the execution order is "
|
||||
"based on the sequence. Low number means high priority."),
|
||||
'model_id': fields.many2one('ir.model', 'Base Model', required=True, ondelete='cascade',
|
||||
help="Base model on which the server action runs."),
|
||||
'menu_ir_values_id': fields.many2one('ir.values', 'More Menu entry', readonly=True,
|
||||
help='More menu entry.'),
|
||||
# Client Action
|
||||
'action_id': fields.many2one('ir.actions.actions', 'Client Action',
|
||||
help="Select the client action that has to be executed."),
|
||||
# Python code
|
||||
'code': fields.text('Python Code',
|
||||
help="Write Python code that the action will execute. Some variables are "
|
||||
"available for use; help about pyhon expression is given in the help tab."),
|
||||
# Workflow signal
|
||||
'use_relational_model': fields.selection([('base', 'Use the base model of the action'),
|
||||
('relational', 'Use a relation field on the base model')],
|
||||
string='Target Model', required=True),
|
||||
'wkf_transition_id': fields.many2one('workflow.transition', string='Signal to Trigger',
|
||||
help="Select the workflow signal to trigger."),
|
||||
'wkf_model_id': fields.many2one('ir.model', 'Target Model',
|
||||
help="The model that will receive the workflow signal. Note that it should have a workflow associated with it."),
|
||||
'wkf_model_name': fields.related('wkf_model_id', 'model', type='char', string='Target Model Name', store=True, readonly=True),
|
||||
'wkf_field_id': fields.many2one('ir.model.fields', string='Relation Field',
|
||||
oldname='trigger_obj_id',
|
||||
help="The field on the current object that links to the target object record (must be a many2one, or an integer field with the record ID)"),
|
||||
# Multi
|
||||
'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions',
|
||||
'server_id', 'action_id',
|
||||
string='Child Actions',
|
||||
help='Child server actions that will be executed. Note that the last return returned action value will be used as global return value.'),
|
||||
# Create/Copy/Write
|
||||
'use_create': fields.selection([('new', 'Create a new record in the Base Model'),
|
||||
('new_other', 'Create a new record in another model'),
|
||||
('copy_current', 'Copy the current record'),
|
||||
('copy_other', 'Choose and copy a record in the database')],
|
||||
string="Creation Policy", required=True,
|
||||
help=""),
|
||||
'crud_model_id': fields.many2one('ir.model', 'Target Model',
|
||||
oldname='srcmodel_id',
|
||||
help="Model for record creation / update. Set this field only to specify a different model than the base model."),
|
||||
'crud_model_name': fields.related('crud_model_id', 'model', type='char',
|
||||
string='Create/Write Target Model Name',
|
||||
store=True, readonly=True),
|
||||
'ref_object': fields.reference('Reference record', selection=_select_objects, size=128,
|
||||
oldname='copy_object'),
|
||||
'link_new_record': fields.boolean('Attach the new record',
|
||||
help="Check this if you want to link the newly-created record "
|
||||
"to the current record on which the server action runs."),
|
||||
'link_field_id': fields.many2one('ir.model.fields', 'Link using field',
|
||||
oldname='record_id',
|
||||
help="Provide the field where the record id is stored after the operations."),
|
||||
'use_write': fields.selection([('current', 'Update the current record'),
|
||||
('expression', 'Update a record linked to the current record using python'),
|
||||
('other', 'Choose and Update a record in the database')],
|
||||
string='Update Policy', required=True,
|
||||
help=""),
|
||||
'write_expression': fields.char('Expression',
|
||||
oldname='write_id',
|
||||
help="Provide an expression that, applied on the current record, gives the field to update."),
|
||||
'fields_lines': fields.one2many('ir.server.object.lines', 'server_id',
|
||||
string='Value Mapping',
|
||||
help=""),
|
||||
|
||||
# Fake fields used to implement the placeholder assistant
|
||||
'model_object_field': fields.many2one('ir.model.fields', string="Field",
|
||||
help="Select target field from the related document model.\n"
|
||||
"If it is a relationship field you will be able to select "
|
||||
"a target field at the destination of the relationship."),
|
||||
'sub_object': fields.many2one('ir.model', 'Sub-model', readonly=True,
|
||||
help="When a relationship field is selected as first field, "
|
||||
"this field shows the document model the relationship goes to."),
|
||||
'sub_model_object_field': fields.many2one('ir.model.fields', 'Sub-field',
|
||||
help="When a relationship field is selected as first field, "
|
||||
"this field lets you select the target field within the "
|
||||
"destination document model (sub-model)."),
|
||||
'copyvalue': fields.char('Placeholder Expression', help="Final placeholder expression, to be copy-pasted in the desired template field."),
|
||||
# Fake fields used to implement the ID finding assistant
|
||||
'id_object': fields.reference('Record', selection=_select_objects, size=128),
|
||||
'id_value': fields.char('Record ID'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'state': 'dummy',
|
||||
'state': 'code',
|
||||
'condition': 'True',
|
||||
'type': 'ir.actions.server',
|
||||
'sequence': 5,
|
||||
|
@ -551,246 +607,427 @@ class actions_server(osv.osv):
|
|||
# - self: ORM model of the record on which the action is triggered
|
||||
# - object: browse_record of the record on which the action is triggered if there is one, otherwise None
|
||||
# - pool: ORM model pool (i.e. self.pool)
|
||||
# - time: Python time module
|
||||
# - cr: database cursor
|
||||
# - uid: current user id
|
||||
# - context: current context
|
||||
# If you plan to return an action, assign: action = {...}
|
||||
""",
|
||||
# - time: Python time module
|
||||
# If you plan to return an action, assign: action = {...}""",
|
||||
'use_relational_model': 'base',
|
||||
'use_create': 'new',
|
||||
'use_write': 'current',
|
||||
}
|
||||
|
||||
def get_email(self, cr, uid, action, context):
|
||||
def _check_expression(self, cr, uid, expression, model_id, context):
|
||||
""" Check python expression (condition, write_expression). Each step of
|
||||
the path must be a valid many2one field, or an integer field for the last
|
||||
step.
|
||||
|
||||
:param str expression: a python expression, beginning by 'obj' or 'object'
|
||||
:param int model_id: the base model of the server action
|
||||
:returns tuple: (is_valid, target_model_name, error_msg)
|
||||
"""
|
||||
if not model_id:
|
||||
return (False, None, 'Your expression cannot be validated because the Base Model is not set.')
|
||||
# fetch current model
|
||||
current_model_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model
|
||||
# transform expression into a path that should look like 'object.many2onefield.many2onefield'
|
||||
path = expression.split('.')
|
||||
initial = path.pop(0)
|
||||
if initial not in ['obj', 'object']:
|
||||
return (False, None, 'Your expression should begin with obj or object.\nAn expression builder is available in the help tab.')
|
||||
# analyze path
|
||||
while path:
|
||||
step = path.pop(0)
|
||||
column_info = self.pool[current_model_name]._all_columns.get(step)
|
||||
if not column_info:
|
||||
return (False, None, 'Part of the expression (%s) is not recognized as a column in the model %s.' % (step, current_model_name))
|
||||
column_type = column_info.column._type
|
||||
if column_type not in ['many2one', 'int']:
|
||||
return (False, None, 'Part of the expression (%s) is not a valid column type (is %s, should be a many2one or an int)' % (step, column_type))
|
||||
if column_type == 'int' and path:
|
||||
return (False, None, 'Part of the expression (%s) is an integer field that is only allowed at the end of an expression' % (step))
|
||||
if column_type == 'many2one':
|
||||
current_model_name = column_info.column._obj
|
||||
return (True, current_model_name, None)
|
||||
|
||||
def _check_write_expression(self, cr, uid, ids, context=None):
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
if record.write_expression and record.model_id:
|
||||
correct, model_name, message = self._check_expression(cr, uid, record.write_expression, record.model_id.id, context=context)
|
||||
if not correct:
|
||||
_logger.warning('Invalid expression: %s' % message)
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_write_expression,
|
||||
'Incorrect Write Record Expression',
|
||||
['write_expression']),
|
||||
(partial(osv.Model._check_m2m_recursion, field_name='child_ids'),
|
||||
'Recursion found in child server actions',
|
||||
['child_ids']),
|
||||
]
|
||||
|
||||
def on_change_model_id(self, cr, uid, ids, model_id, wkf_model_id, crud_model_id, context=None):
|
||||
""" When changing the action base model, reset workflow and crud config
|
||||
to ease value coherence. """
|
||||
values = {
|
||||
'use_create': 'new',
|
||||
'use_write': 'current',
|
||||
'use_relational_model': 'base',
|
||||
'wkf_model_id': model_id,
|
||||
'wkf_field_id': False,
|
||||
'crud_model_id': model_id,
|
||||
}
|
||||
return {'value': values}
|
||||
|
||||
def on_change_wkf_wonfig(self, cr, uid, ids, use_relational_model, wkf_field_id, wkf_model_id, model_id, context=None):
|
||||
""" Update workflow type configuration
|
||||
|
||||
- update the workflow model (for base (model_id) /relational (field.relation))
|
||||
- update wkf_transition_id to False if workflow model changes, to force
|
||||
the user to choose a new one
|
||||
"""
|
||||
values = {}
|
||||
if use_relational_model == 'relational' and wkf_field_id:
|
||||
field = self.pool['ir.model.fields'].browse(cr, uid, wkf_field_id, context=context)
|
||||
new_wkf_model_id = self.pool.get('ir.model').search(cr, uid, [('model', '=', field.relation)], context=context)[0]
|
||||
values['wkf_model_id'] = new_wkf_model_id
|
||||
else:
|
||||
values['wkf_model_id'] = model_id
|
||||
return {'value': values}
|
||||
|
||||
def on_change_wkf_model_id(self, cr, uid, ids, wkf_model_id, context=None):
|
||||
""" When changing the workflow model, update its stored name also """
|
||||
wkf_model_name = False
|
||||
if wkf_model_id:
|
||||
wkf_model_name = self.pool.get('ir.model').browse(cr, uid, wkf_model_id, context).model
|
||||
values = {'wkf_transition_id': False, 'wkf_model_name': wkf_model_name}
|
||||
return {'value': values}
|
||||
|
||||
def on_change_crud_config(self, cr, uid, ids, state, use_create, use_write, ref_object, crud_model_id, model_id, context=None):
|
||||
""" Wrapper on CRUD-type (create or write) on_change """
|
||||
if state == 'object_create':
|
||||
return self.on_change_create_config(cr, uid, ids, use_create, ref_object, crud_model_id, model_id, context=context)
|
||||
elif state == 'object_write':
|
||||
return self.on_change_write_config(cr, uid, ids, use_write, ref_object, crud_model_id, model_id, context=context)
|
||||
else:
|
||||
return {}
|
||||
|
||||
def on_change_create_config(self, cr, uid, ids, use_create, ref_object, crud_model_id, model_id, context=None):
|
||||
""" When changing the object_create type configuration:
|
||||
|
||||
- `new` and `copy_current`: crud_model_id is the same as base model
|
||||
- `new_other`: user choose crud_model_id
|
||||
- `copy_other`: disassemble the reference object to have its model
|
||||
- if the target model has changed, then reset the link field that is
|
||||
probably not correct anymore
|
||||
"""
|
||||
values = {}
|
||||
if use_create == 'new':
|
||||
values['crud_model_id'] = model_id
|
||||
elif use_create == 'new_other':
|
||||
pass
|
||||
elif use_create == 'copy_current':
|
||||
values['crud_model_id'] = model_id
|
||||
elif use_create == 'copy_other' and ref_object:
|
||||
ref_model, ref_id = ref_object.split(',')
|
||||
ref_model_id = self.pool['ir.model'].search(cr, uid, [('model', '=', ref_model)], context=context)[0]
|
||||
values['crud_model_id'] = ref_model_id
|
||||
|
||||
if values.get('crud_model_id') != crud_model_id:
|
||||
values['link_field_id'] = False
|
||||
return {'value': values}
|
||||
|
||||
def on_change_write_config(self, cr, uid, ids, use_write, ref_object, crud_model_id, model_id, context=None):
|
||||
""" When changing the object_write type configuration:
|
||||
|
||||
- `current`: crud_model_id is the same as base model
|
||||
- `other`: disassemble the reference object to have its model
|
||||
- `expression`: has its own on_change, nothing special here
|
||||
"""
|
||||
values = {}
|
||||
if use_write == 'current':
|
||||
values['crud_model_id'] = model_id
|
||||
elif use_write == 'other' and ref_object:
|
||||
ref_model, ref_id = ref_object.split(',')
|
||||
ref_model_id = self.pool['ir.model'].search(cr, uid, [('model', '=', ref_model)], context=context)[0]
|
||||
values['crud_model_id'] = ref_model_id
|
||||
elif use_write == 'expression':
|
||||
pass
|
||||
|
||||
if values.get('crud_model_id') != crud_model_id:
|
||||
values['link_field_id'] = False
|
||||
return {'value': values}
|
||||
|
||||
def on_change_write_expression(self, cr, uid, ids, write_expression, model_id, context=None):
|
||||
""" Check the write_expression and update crud_model_id accordingly """
|
||||
values = {}
|
||||
valid, model_name, message = self._check_expression(cr, uid, write_expression, model_id, context=context)
|
||||
if valid:
|
||||
ref_model_id = self.pool['ir.model'].search(cr, uid, [('model', '=', model_name)], context=context)[0]
|
||||
values['crud_model_id'] = ref_model_id
|
||||
return {'value': values}
|
||||
if not message:
|
||||
message = 'Invalid expression'
|
||||
return {
|
||||
'warning': {
|
||||
'title': 'Incorrect expression',
|
||||
'message': message,
|
||||
}
|
||||
}
|
||||
|
||||
def on_change_crud_model_id(self, cr, uid, ids, crud_model_id, context=None):
|
||||
""" When changing the CRUD model, update its stored name also """
|
||||
crud_model_name = False
|
||||
if crud_model_id:
|
||||
crud_model_name = self.pool.get('ir.model').browse(cr, uid, crud_model_id, context).model
|
||||
values = {'link_field_id': False, 'crud_model_name': crud_model_name}
|
||||
return {'value': values}
|
||||
|
||||
def _build_expression(self, field_name, sub_field_name):
|
||||
""" Returns a placeholder expression for use in a template field,
|
||||
based on the values provided in the placeholder assistant.
|
||||
|
||||
:param field_name: main field name
|
||||
:param sub_field_name: sub field name (M2O)
|
||||
:return: final placeholder expression
|
||||
"""
|
||||
expression = ''
|
||||
if field_name:
|
||||
expression = "object." + field_name
|
||||
if sub_field_name:
|
||||
expression += "." + sub_field_name
|
||||
return expression
|
||||
|
||||
def onchange_sub_model_object_value_field(self, cr, uid, ids, model_object_field, sub_model_object_field=False, context=None):
|
||||
result = {
|
||||
'sub_object': False,
|
||||
'copyvalue': False,
|
||||
'sub_model_object_field': False,
|
||||
}
|
||||
if model_object_field:
|
||||
fields_obj = self.pool.get('ir.model.fields')
|
||||
field_value = fields_obj.browse(cr, uid, model_object_field, context)
|
||||
if field_value.ttype in ['many2one', 'one2many', 'many2many']:
|
||||
res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_value.relation)], context=context)
|
||||
sub_field_value = False
|
||||
if sub_model_object_field:
|
||||
sub_field_value = fields_obj.browse(cr, uid, sub_model_object_field, context)
|
||||
if res_ids:
|
||||
result.update({
|
||||
'sub_object': res_ids[0],
|
||||
'copyvalue': self._build_expression(field_value.name, sub_field_value and sub_field_value.name or False),
|
||||
'sub_model_object_field': sub_model_object_field or False,
|
||||
})
|
||||
else:
|
||||
result.update({
|
||||
'copyvalue': self._build_expression(field_value.name, False),
|
||||
})
|
||||
return {'value': result}
|
||||
|
||||
def onchange_id_object(self, cr, uid, ids, id_object, context=None):
|
||||
if id_object:
|
||||
ref_model, ref_id = id_object.split(',')
|
||||
return {'value': {'id_value': ref_id}}
|
||||
return {'value': {'id_value': False}}
|
||||
|
||||
def create_action(self, cr, uid, ids, context=None):
|
||||
""" Create a contextual action for each of the server actions. """
|
||||
for action in self.browse(cr, uid, ids, context=context):
|
||||
ir_values_id = self.pool.get('ir.values').create(cr, SUPERUSER_ID, {
|
||||
'name': _('Run %s') % action.name,
|
||||
'model': action.model_id.model,
|
||||
'key2': 'client_action_multi',
|
||||
'value': "ir.actions.server,%s" % action.id,
|
||||
}, context)
|
||||
action.write({
|
||||
'menu_ir_values_id': ir_values_id,
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
def unlink_action(self, cr, uid, ids, context=None):
|
||||
""" Remove the contextual actions created for the server actions. """
|
||||
for action in self.browse(cr, uid, ids, context=context):
|
||||
if action.menu_ir_values_id:
|
||||
try:
|
||||
self.pool.get('ir.values').unlink(cr, SUPERUSER_ID, action.menu_ir_values_id.id, context)
|
||||
except Exception:
|
||||
raise osv.except_osv(_('Warning'), _('Deletion of the action record failed.'))
|
||||
return True
|
||||
|
||||
def run_action_client_action(self, cr, uid, action, eval_context=None, context=None):
|
||||
if not action.action_id:
|
||||
raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
|
||||
return self.pool[action.action_id.type].read(cr, uid, action.action_id.id, context=context)
|
||||
|
||||
def run_action_code(self, cr, uid, action, eval_context=None, context=None):
|
||||
eval(action.code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action'
|
||||
if 'action' in eval_context:
|
||||
return eval_context['action']
|
||||
|
||||
def run_action_trigger(self, cr, uid, action, eval_context=None, context=None):
|
||||
""" Trigger a workflow signal, depending on the use_relational_model:
|
||||
|
||||
- `base`: base_model_pool.signal_<TRIGGER_NAME>(cr, uid, context.get('active_id'))
|
||||
- `relational`: find the related model and object, using the relational
|
||||
field, then target_model_pool.signal_<TRIGGER_NAME>(cr, uid, target_id)
|
||||
"""
|
||||
obj_pool = self.pool[action.model_id.model]
|
||||
id = context.get('active_id')
|
||||
obj = obj_pool.browse(cr, uid, id)
|
||||
if action.use_relational_model == 'base':
|
||||
target_id = context.get('active_id')
|
||||
target_pool = obj_pool
|
||||
else:
|
||||
value = getattr(obj_pool.browse(cr, uid, context.get('active_id'), context=context), action.wkf_field_id.name)
|
||||
if action.wkf_field_id.ttype == 'many2one':
|
||||
target_id = value.id
|
||||
else:
|
||||
target_id = value
|
||||
target_pool = self.pool[action.wkf_model_id.model]
|
||||
|
||||
fields = None
|
||||
trigger_name = action.wkf_transition_id.signal
|
||||
|
||||
if '/' in action.email.complete_name:
|
||||
fields = action.email.complete_name.split('/')
|
||||
elif '.' in action.email.complete_name:
|
||||
fields = action.email.complete_name.split('.')
|
||||
workflow.trg_validate(uid, target_pool._name, target_id, trigger_name, cr)
|
||||
|
||||
for field in fields:
|
||||
try:
|
||||
obj = getattr(obj, field)
|
||||
except Exception:
|
||||
_logger.exception('Failed to parse: %s', field)
|
||||
def run_action_multi(self, cr, uid, action, eval_context=None, context=None):
|
||||
res = []
|
||||
for act in action.child_ids:
|
||||
result = self.run(cr, uid, [act.id], context)
|
||||
if result:
|
||||
res.append(result)
|
||||
return res and res[0] or False
|
||||
|
||||
return obj
|
||||
def run_action_object_write(self, cr, uid, action, eval_context=None, context=None):
|
||||
""" Write server action.
|
||||
|
||||
def get_mobile(self, cr, uid, action, context):
|
||||
obj_pool = self.pool[action.model_id.model]
|
||||
id = context.get('active_id')
|
||||
obj = obj_pool.browse(cr, uid, id)
|
||||
- 1. evaluate the value mapping
|
||||
- 2. depending on the write configuration:
|
||||
|
||||
fields = None
|
||||
- `current`: id = active_id
|
||||
- `other`: id = from reference object
|
||||
- `expression`: id = from expression evaluation
|
||||
"""
|
||||
res = {}
|
||||
for exp in action.fields_lines:
|
||||
if exp.type == 'equation':
|
||||
expr = eval(exp.value, eval_context)
|
||||
else:
|
||||
expr = exp.value
|
||||
res[exp.col1.name] = expr
|
||||
|
||||
if '/' in action.mobile.complete_name:
|
||||
fields = action.mobile.complete_name.split('/')
|
||||
elif '.' in action.mobile.complete_name:
|
||||
fields = action.mobile.complete_name.split('.')
|
||||
if action.use_write == 'current':
|
||||
model = action.model_id.model
|
||||
ref_id = context.get('active_id')
|
||||
elif action.use_write == 'other':
|
||||
model = action.crud_model_id.model
|
||||
ref_id = action.ref_object.id
|
||||
elif action.use_write == 'expression':
|
||||
model = action.crud_model_id.model
|
||||
ref = eval(action.write_expression, eval_context)
|
||||
if isinstance(ref, browse_record):
|
||||
ref_id = getattr(ref, 'id')
|
||||
else:
|
||||
ref_id = int(ref)
|
||||
|
||||
for field in fields:
|
||||
try:
|
||||
obj = getattr(obj, field)
|
||||
except Exception:
|
||||
_logger.exception('Failed to parse: %s', field)
|
||||
obj_pool = self.pool[model]
|
||||
obj_pool.write(cr, uid, [ref_id], res, context=context)
|
||||
|
||||
return obj
|
||||
def run_action_object_create(self, cr, uid, action, eval_context=None, context=None):
|
||||
""" Create and Copy server action.
|
||||
|
||||
def merge_message(self, cr, uid, keystr, action, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
- 1. evaluate the value mapping
|
||||
- 2. depending on the write configuration:
|
||||
|
||||
def merge(match):
|
||||
obj_pool = self.pool[action.model_id.model]
|
||||
id = context.get('active_id')
|
||||
obj = obj_pool.browse(cr, uid, id)
|
||||
exp = str(match.group()[2:-2]).strip()
|
||||
result = eval(exp,
|
||||
{
|
||||
'object': obj,
|
||||
'context': dict(context), # copy context to prevent side-effects of eval
|
||||
'time': time,
|
||||
})
|
||||
if result in (None, False):
|
||||
return str("--------")
|
||||
return tools.ustr(result)
|
||||
- `new`: new record in the base model
|
||||
- `copy_current`: copy the current record (id = active_id) + gives custom values
|
||||
- `new_other`: new record in target model
|
||||
- `copy_other`: copy the current record (id from reference object)
|
||||
+ gives custom values
|
||||
"""
|
||||
res = {}
|
||||
for exp in action.fields_lines:
|
||||
if exp.type == 'equation':
|
||||
expr = eval(exp.value, eval_context)
|
||||
else:
|
||||
expr = exp.value
|
||||
res[exp.col1.name] = expr
|
||||
|
||||
com = re.compile('(\[\[.+?\]\])')
|
||||
message = com.sub(merge, keystr)
|
||||
if action.use_create in ['new', 'copy_current']:
|
||||
model = action.model_id.model
|
||||
elif action.use_create in ['new_other', 'copy_other']:
|
||||
model = action.crud_model_id.model
|
||||
|
||||
return message
|
||||
obj_pool = self.pool[model]
|
||||
if action.use_create == 'copy_current':
|
||||
ref_id = context.get('active_id')
|
||||
res_id = obj_pool.copy(cr, uid, ref_id, res, context=context)
|
||||
elif action.use_create == 'copy_other':
|
||||
ref_id = action.ref_object.id
|
||||
res_id = obj_pool.copy(cr, uid, ref_id, res, context=context)
|
||||
else:
|
||||
res_id = obj_pool.create(cr, uid, res, context=context)
|
||||
|
||||
# Context should contains:
|
||||
# ids : original ids
|
||||
# id : current id of the object
|
||||
# OUT:
|
||||
# False : Finished correctly
|
||||
# ACTION_ID : Action to launch
|
||||
if action.link_new_record and action.link_field_id:
|
||||
self.pool[action.model_id.model].write(cr, uid, [context.get('active_id')], {action.link_field_id.name: res_id})
|
||||
|
||||
# FIXME: refactor all the eval() calls in run()!
|
||||
def run(self, cr, uid, ids, context=None):
|
||||
""" Run the server action. For each server action, the condition is
|
||||
checked. Note that A void (aka False) condition is considered as always
|
||||
valid. If it is verified, the run_action_<STATE> method is called. This
|
||||
allows easy inheritance of the server actions.
|
||||
|
||||
:param dict context: context should contain following keys
|
||||
|
||||
- active_id: id of the current object (single mode)
|
||||
- active_model: current model that should equal the action's model
|
||||
|
||||
The following keys are optional:
|
||||
|
||||
- active_ids: ids of the current records (mass mode). If active_ids
|
||||
and active_id are present, active_ids is given precedence.
|
||||
|
||||
:return: an action_id to be executed, or False is finished correctly without
|
||||
return action
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
res = False
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid)
|
||||
active_ids = context.get('active_ids', [context.get('active_id', None)])
|
||||
for action in self.browse(cr, uid, ids, context):
|
||||
obj = None
|
||||
obj_pool = self.pool[action.model_id.model]
|
||||
if context.get('active_model') == action.model_id.model and context.get('active_id'):
|
||||
obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
|
||||
cxt = {
|
||||
'self': obj_pool,
|
||||
'object': obj,
|
||||
'obj': obj,
|
||||
'pool': self.pool,
|
||||
'time': time,
|
||||
'cr': cr,
|
||||
'context': dict(context), # copy context to prevent side-effects of eval
|
||||
'uid': uid,
|
||||
'user': user
|
||||
}
|
||||
expr = eval(str(action.condition), cxt)
|
||||
if not expr:
|
||||
continue
|
||||
for active_id in active_ids:
|
||||
if context.get('active_model') == action.model_id.model and active_id:
|
||||
obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
|
||||
# run context dedicated to a particular active_id
|
||||
run_context = dict(context, active_ids=[active_id], active_id=active_id)
|
||||
# evaluation context for python strings to evaluate
|
||||
eval_context = {
|
||||
'self': obj_pool,
|
||||
'object': obj,
|
||||
'obj': obj,
|
||||
'pool': self.pool,
|
||||
'time': time,
|
||||
'cr': cr,
|
||||
'context': dict(run_context), # copy context to prevent side-effects of eval
|
||||
'uid': uid,
|
||||
'user': user
|
||||
}
|
||||
|
||||
if action.state=='client_action':
|
||||
if not action.action_id:
|
||||
raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
|
||||
return self.pool[action.action_id.type].read(cr, uid, action.action_id.id, context=context)
|
||||
|
||||
if action.state=='code':
|
||||
eval(action.code.strip(), cxt, mode="exec", nocopy=True) # nocopy allows to return 'action'
|
||||
if 'action' in cxt:
|
||||
return cxt['action']
|
||||
|
||||
if action.state == 'email':
|
||||
email_from = config['email_from']
|
||||
if not email_from:
|
||||
_logger.debug('--email-from command line option is not specified, using a fallback value instead.')
|
||||
if user.email:
|
||||
email_from = user.email
|
||||
else:
|
||||
email_from = "%s@%s" % (user.login, gethostname())
|
||||
|
||||
try:
|
||||
address = eval(str(action.email), cxt)
|
||||
except Exception:
|
||||
address = str(action.email)
|
||||
|
||||
if not address:
|
||||
_logger.info('No partner email address specified, not sending any email.')
|
||||
# evaluate the condition, with the specific case that a void (aka False) condition is considered as True
|
||||
condition = action.condition
|
||||
if action.condition is False:
|
||||
condition = True
|
||||
expr = eval(str(condition), eval_context)
|
||||
if not expr:
|
||||
continue
|
||||
# call the method related to the action: run_action_<STATE>
|
||||
if hasattr(self, 'run_action_%s' % action.state):
|
||||
res = getattr(self, 'run_action_%s' % action.state)(cr, uid, action, eval_context=eval_context, context=run_context)
|
||||
return res
|
||||
|
||||
# handle single and multiple recipient addresses
|
||||
addresses = address if isinstance(address, (tuple, list)) else [address]
|
||||
subject = self.merge_message(cr, uid, action.subject, action, context)
|
||||
body = self.merge_message(cr, uid, action.message, action, context)
|
||||
|
||||
ir_mail_server = self.pool.get('ir.mail_server')
|
||||
msg = ir_mail_server.build_email(email_from, addresses, subject, body)
|
||||
res_email = ir_mail_server.send_email(cr, uid, msg)
|
||||
if res_email:
|
||||
_logger.info('Email successfully sent to: %s', addresses)
|
||||
else:
|
||||
_logger.warning('Failed to send email to: %s', addresses)
|
||||
|
||||
if action.state == 'trigger':
|
||||
model = action.wkf_model_id.model
|
||||
m2o_field_name = action.trigger_obj_id.name
|
||||
target_id = obj_pool.read(cr, uid, context.get('active_id'), [m2o_field_name])[m2o_field_name]
|
||||
target_id = target_id[0] if isinstance(target_id,tuple) else target_id
|
||||
openerp.workflow.trg_validate(uid, model, int(target_id), action.trigger_name, cr)
|
||||
|
||||
if action.state == 'sms':
|
||||
#TODO: set the user and password from the system
|
||||
# for the sms gateway user / password
|
||||
# USE smsclient module from extra-addons
|
||||
_logger.warning('SMS Facility has not been implemented yet. Use smsclient module!')
|
||||
|
||||
if action.state == 'other':
|
||||
res = []
|
||||
for act in action.child_ids:
|
||||
context['active_id'] = context['active_ids'][0]
|
||||
result = self.run(cr, uid, [act.id], context)
|
||||
if result:
|
||||
res.append(result)
|
||||
return res
|
||||
|
||||
if action.state == 'loop':
|
||||
expr = eval(str(action.expression), cxt)
|
||||
context['object'] = obj
|
||||
for i in expr:
|
||||
context['active_id'] = i.id
|
||||
self.run(cr, uid, [action.loop_action.id], context)
|
||||
|
||||
if action.state == 'object_write':
|
||||
res = {}
|
||||
for exp in action.fields_lines:
|
||||
euq = exp.value
|
||||
if exp.type == 'equation':
|
||||
expr = eval(euq, cxt)
|
||||
else:
|
||||
expr = exp.value
|
||||
res[exp.col1.name] = expr
|
||||
|
||||
if not action.write_id:
|
||||
if not action.srcmodel_id:
|
||||
obj_pool = self.pool[action.model_id.model]
|
||||
obj_pool.write(cr, uid, [context.get('active_id')], res)
|
||||
else:
|
||||
write_id = context.get('active_id')
|
||||
obj_pool = self.pool[action.srcmodel_id.model]
|
||||
obj_pool.write(cr, uid, [write_id], res)
|
||||
|
||||
elif action.write_id:
|
||||
obj_pool = self.pool[action.srcmodel_id.model]
|
||||
rec = self.pool[action.model_id.model].browse(cr, uid, context.get('active_id'))
|
||||
id = eval(action.write_id, {'object': rec})
|
||||
try:
|
||||
id = int(id)
|
||||
except:
|
||||
raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
|
||||
|
||||
if type(id) != type(1):
|
||||
raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
|
||||
write_id = id
|
||||
obj_pool.write(cr, uid, [write_id], res)
|
||||
|
||||
if action.state == 'object_create':
|
||||
res = {}
|
||||
for exp in action.fields_lines:
|
||||
euq = exp.value
|
||||
if exp.type == 'equation':
|
||||
expr = eval(euq, cxt)
|
||||
else:
|
||||
expr = exp.value
|
||||
res[exp.col1.name] = expr
|
||||
|
||||
obj_pool = self.pool[action.srcmodel_id.model]
|
||||
res_id = obj_pool.create(cr, uid, res)
|
||||
if action.record_id:
|
||||
self.pool[action.model_id.model].write(cr, uid, [context.get('active_id')], {action.record_id.name:res_id})
|
||||
|
||||
if action.state == 'object_copy':
|
||||
res = {}
|
||||
for exp in action.fields_lines:
|
||||
euq = exp.value
|
||||
if exp.type == 'equation':
|
||||
expr = eval(euq, cxt)
|
||||
else:
|
||||
expr = exp.value
|
||||
res[exp.col1.name] = expr
|
||||
|
||||
model = action.copy_object.split(',')[0]
|
||||
cid = action.copy_object.split(',')[1]
|
||||
obj_pool = self.pool[model]
|
||||
obj_pool.copy(cr, uid, int(cid), res)
|
||||
|
||||
return False
|
||||
|
||||
actions_server()
|
||||
|
||||
class act_window_close(osv.osv):
|
||||
_name = 'ir.actions.act_window_close'
|
||||
|
|
|
@ -309,86 +309,208 @@
|
|||
<field name="model">ir.actions.server</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Server Action" version="7.0">
|
||||
<group>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
</div>
|
||||
<div class="oe_right oe_button_box" name="buttons">
|
||||
<field name="menu_ir_values_id" invisible="1"/>
|
||||
<button name="create_action" string="Add in the 'More' menu" type="object"
|
||||
attrs="{'invisible':[('menu_ir_values_id','!=',False)]}"
|
||||
help="Display an option on related documents to run this sever action"/>
|
||||
<button name="unlink_action" string="Remove from the 'More' menu" type="object"
|
||||
attrs="{'invisible':[('menu_ir_values_id','=',False)]}"
|
||||
help="Remove the contextual action related to this server action"/>
|
||||
</div>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="model_id"/>
|
||||
<field name="state"/>
|
||||
<group>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="model_id"
|
||||
on_change="on_change_model_id(model_id, wkf_model_id, crud_model_id)"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="condition"/>
|
||||
<field name="sequence"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="condition"/>
|
||||
<field name="sequence"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Python Code" attrs="{'invisible':[('state','!=','code')]}">
|
||||
<field name="code"/>
|
||||
</page>
|
||||
<page string="Trigger" attrs="{'invisible':[('state','!=','trigger')]}">
|
||||
<group string="Trigger Configuration" col="4">
|
||||
<field name="wkf_model_id" attrs="{'required':[('state','=','trigger')]}"/>
|
||||
<field name="trigger_obj_id" context="{'key':''}"
|
||||
domain="[('model_id','=',model_id),('ttype','in',['many2one','int'])]"
|
||||
attrs="{'required':[('state','=','trigger')]}"/>
|
||||
<field name="trigger_name" attrs="{'required':[('state','=','trigger')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Action to Launch" attrs="{'invisible':[('state','!=','client_action')]}">
|
||||
<group>
|
||||
<field name="action_id" attrs="{'required':[('state','=','client_action')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Email Configuration" attrs="{'invisible':[('state','!=','email')]}">
|
||||
<group>
|
||||
<field name="email" domain="[('model_id','=',model_id)]" attrs="{'required':[('state','=','email')]}"/>
|
||||
<field name="subject" attrs="{'required':[('state','=','email')]}"/>
|
||||
<field name="message" attrs="{'required':[('state','=','email')]}"/>
|
||||
<newline/>
|
||||
<label colspan="2" string="Access all the fields related to the current object using expressions, i.e. object.partner_id.name " align="0.0"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="SMS Configuration" attrs="{'invisible':[('state','!=','sms')]}">
|
||||
<group>
|
||||
<field name="mobile" domain="[('model_id','=',model_id)]" attrs="{'required':[('state','=','sms')]}"/>
|
||||
<field name="sms" attrs="{'required':[('state','=','sms')]}"/>
|
||||
</group>
|
||||
<label string="Access all the fields related to the current object using expressions, i.e. object.partner_id.name " align="0.0"/>
|
||||
</page>
|
||||
<page string="Create / Write / Copy" attrs="{'invisible':[('state','!=','object_create'), ('state','!=','object_write'), ('state','!=','object_copy')]}">
|
||||
<group col="4" string="Fields Mapping">
|
||||
<field name="srcmodel_id" attrs="{'required':[('state','!=','dummy'), ('state','!=','sms'), ('state','!=','code'), ('state','!=','loop'), ('state','!=','trigger'), ('state','!=','object_copy'), ('state','!=','client_action'), ('state','!=','email'), ('state','!=','sms'), ('state','!=','other')]}"/>
|
||||
<field name="copy_object" on_change="change_object(copy_object, state)" attrs="{'required':[('state','!=','dummy'), ('state','!=','sms'), ('state','!=','code'), ('state','!=','loop'), ('state','!=','trigger'), ('state','!=','object_write'), ('state','!=','object_create'), ('state','!=','client_action'), ('state','!=','email'), ('state','!=','sms'), ('state','!=','other')]}"/>
|
||||
<field name="fields_lines" nolabel="1" colspan="2">
|
||||
<tree string="Field Mappings" editable="top">
|
||||
<field name="col1" domain="[('model_id','=',parent.srcmodel_id or parent.model_id)]"/>
|
||||
<field name="type"/>
|
||||
<field name="value" colspan="4"/>
|
||||
</tree>
|
||||
<form string="Field Mapping" version="7.0">
|
||||
<group col="4">
|
||||
<field name="col1" domain="[('model_id','=',parent.srcmodel_id or parent.model_id)]"/>
|
||||
<field name="type"/>
|
||||
<field name="value" colspan="4"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Python Code" name='code' autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'code')]}">
|
||||
<field name="code" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
|
||||
</page>
|
||||
|
||||
<page string="Worflow Signal" autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'trigger')]}">
|
||||
<p attrs="{'invisible': [('model_id', '!=', False)]}">
|
||||
Please set the Base Model before setting the action details.
|
||||
</p>
|
||||
<group attrs="{'invisible': [('model_id', '=', False)]}">
|
||||
<field name="use_relational_model" widget="radio"
|
||||
on_change="on_change_wkf_wonfig(use_relational_model, wkf_field_id, wkf_model_id, model_id)"
|
||||
attrs="{'readonly': [('model_id', '=', False)]}"/>
|
||||
<field name="wkf_field_id" context="{'key': ''}"
|
||||
on_change="on_change_wkf_wonfig(use_relational_model, wkf_field_id, wkf_model_id, model_id)"
|
||||
attrs="{'required': [('state', '=', 'trigger'), ('use_relational_model', '=', 'relational')],
|
||||
'invisible': [('use_relational_model', '=', 'base')]}"
|
||||
domain="[('model_id', '=', model_id), ('ttype', 'in', ['many2one'])]"/>
|
||||
<field name="wkf_model_id" invisible="1"
|
||||
on_change="on_change_wkf_model_id(wkf_model_id)"/>
|
||||
<field name="wkf_model_name" invisible="1"/>
|
||||
<field name="wkf_transition_id" attrs="{'required': [('state', '=', 'trigger')]}"
|
||||
domain="[('wkf_id.osv', '=', wkf_model_name)]"/>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<page string="Client" autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'client_action')]}">
|
||||
<group>
|
||||
<field name="action_id" attrs="{'required':[('state', '=', 'client_action')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
|
||||
<page string="Create / Write / Copy" autofocus="autofocus"
|
||||
attrs="{'invisible':[('state', 'not in', ['object_create', 'object_write'])]}">
|
||||
<p attrs="{'invisible': [('model_id', '!=', False)]}">
|
||||
Please set the Base Model before setting the action details.
|
||||
</p>
|
||||
<group attrs="{'invisible': [('model_id', '=', False)]}">
|
||||
<field name="use_create" widget="radio"
|
||||
on_change="on_change_crud_config(state, use_create, use_write, ref_object, crud_model_id, model_id)"
|
||||
attrs="{'invisible': [('state', '!=', 'object_create')]}"/>
|
||||
|
||||
<field name="use_write" widget="radio"
|
||||
on_change="on_change_crud_config(state, use_create, use_write, ref_object, crud_model_id, model_id)"
|
||||
attrs="{'invisible': [('state', '!=', 'object_write')]}"/>
|
||||
|
||||
<label for="ref_object" string=" "
|
||||
attrs="{'invisible': ['&',
|
||||
'|', ('state', '!=', 'object_write'), ('use_write', '!=', 'other'),
|
||||
'|', ('state', '!=', 'object_create'), ('use_create', '!=', 'copy_other')]}"/>
|
||||
<div style="margin-left: 24px;"
|
||||
attrs="{'invisible': ['&',
|
||||
'|', ('state', '!=', 'object_write'), ('use_write', '!=', 'other'),
|
||||
'|', ('state', '!=', 'object_create'), ('use_create', '!=', 'copy_other')]}">
|
||||
<field name="ref_object" nolabel="1"
|
||||
on_change="on_change_crud_config(state, use_create, use_write, ref_object, crud_model_id, model_id)"/>
|
||||
</div>
|
||||
|
||||
<field name="crud_model_id"
|
||||
on_change="on_change_crud_model_id(crud_model_id)"
|
||||
attrs="{'invisible': ['|', ('state', '!=', 'object_create'), ('use_create', '!=', 'new_other')]}"/>
|
||||
<field name="crud_model_name" invisible="1"/>
|
||||
|
||||
<label for="link_new_record" attrs="{'invisible': [('state', '!=', 'object_create')]}"/>
|
||||
<div attrs="{'invisible': [('state', '!=', 'object_create')]}">
|
||||
<field name="link_new_record" nolabel="1" style="display: inline-block;"/>
|
||||
<p class="oe_grey oe_edit_only" style="display: inline-block; margin: 0px 0px 0px 8px;">
|
||||
Check to attach the newly created record to the record on which the server action runs.
|
||||
</p>
|
||||
<group>
|
||||
<field name="link_field_id"
|
||||
domain="[('model_id', '=', model_id), ('relation', '=', crud_model_name), ('ttype', 'in', ['many2one'])]"
|
||||
attrs="{'required': [('state', '=', 'object_create'), ('link_new_record', '=', True)],
|
||||
'invisible': ['|', ('state', '!=', 'object_create'), ('link_new_record', '=', False)]}"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
<field name="record_id" attrs="{'readonly':[('state','!=','object_create')]}" domain="[('model_id','in',[model_id])]"/>
|
||||
<field name="write_id" attrs="{'readonly':[('state','!=','object_write')]}"/>
|
||||
</group>
|
||||
<label string="If you use a formula type, use a python expression using the variable 'object'." align="0.0"/>
|
||||
</page>
|
||||
<page string="Iteration Actions" attrs="{'invisible':[('state','!=','loop')]}">
|
||||
<group col="4">
|
||||
<field name="expression" attrs="{'required':[('state','=','loop')]}"/>
|
||||
<field name="loop_action" domain="[('state','!=','loop')]" attrs="{'required':[('state','=','loop')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Multi Actions" attrs="{'invisible':[('state','!=','other')]}">
|
||||
<field name="child_ids"/>
|
||||
<label string="Only one client action will be executed, last client action will be considered in case of multiple client actions." align="0.0"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<field name="type" readonly="1"/>
|
||||
</div>
|
||||
|
||||
<label for="link_new_record" attrs="{'invisible': ['|', ('state', '!=', 'object_write'), ('use_write', '!=', 'expression')]}"/>
|
||||
<div attrs="{'invisible': ['|', ('state', '!=', 'object_write'), ('use_write', '!=', 'expression')]}">
|
||||
<p class="oe_grey oe_edit_only" style="margin: 0px;">
|
||||
Write a python expression, beginning with object, that gives the record to update. An expression builder is available in the help tab. Examples:
|
||||
</p>
|
||||
<ul class="oe_grey oe_edit_only">
|
||||
<li>object.partner_id</li>
|
||||
<li>object.partner_id.currency_id</li>
|
||||
</ul>
|
||||
<field name="write_expression"
|
||||
on_change="on_change_write_expression(write_expression, model_id)"
|
||||
attrs="{'required': [('state', '=', 'object_write'), ('use_write', '=', 'expression')]}"/>
|
||||
</div>
|
||||
|
||||
<field name="fields_lines">
|
||||
<tree string="Field Mappings" editable="top">
|
||||
<field name="col1" domain="[('model_id', '=', parent.crud_model_id)]"/>
|
||||
<field name="type"/>
|
||||
<field name="value"/>
|
||||
</tree>
|
||||
<form string="Field Mapping" version="7.0">
|
||||
<group >
|
||||
<field name="col1" domain="[('model_id', '=', parent.crud_model_id)]"/>
|
||||
<field name="type"/>
|
||||
<field name="value"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<page string="Execute several actions" autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'multi')]}">
|
||||
<p class="oe_grey">
|
||||
If several child actions return an action, only the last one will be executed.
|
||||
This may happen when having server actions executing code that returns an action, or server actions returning a client action.
|
||||
</p>
|
||||
<field name="child_ids"
|
||||
domain="[('model_id', '=', model_id)]"/>
|
||||
</page>
|
||||
|
||||
<page string="Help">
|
||||
<group>
|
||||
<div style="margin-top: 4px;">
|
||||
<h3>Help with Python expressions.</h3>
|
||||
<p>Various fields may use Python code or Python expressions. The following variables can be used:</p>
|
||||
<ul>
|
||||
<li>self: ORM model of the record on which the action is triggered</li>
|
||||
<li>object or obj: browse_record of the record on which the action is triggered</li>
|
||||
<li>pool: ORM model pool (i.e. self.pool)</li>
|
||||
<li>time: Python time module</li>
|
||||
<li>cr: database cursor</li>
|
||||
<li>uid: current user id</li>
|
||||
<li>context: current context</li>
|
||||
</ul>
|
||||
<div>
|
||||
<p>Example of condition expression using Python</p>
|
||||
<ul>
|
||||
<li>condition: True</li>
|
||||
<li>condition: object.list_price > 5000</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div attrs="{'invisible': [('state', '!=', 'code')]}">
|
||||
<p>Example of python code</p>
|
||||
<code>
|
||||
partner_name = obj.name + '_code'
|
||||
self.pool["res.partner"].create(cr, uid, {"name": partner_name}, context=context)
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<group>
|
||||
<h3 colspan="2">Dynamic expression builder</h3>
|
||||
<p colspan="2" attrs="{'invisible': [('model_id', '!=', False)]}">
|
||||
Please set the Base Model of the action to enable the dynamic expression buidler.
|
||||
</p>
|
||||
<field name="model_object_field"
|
||||
attrs="{'invisible': [('model_id', '=', False)]}"
|
||||
domain="[('model_id', '=', model_id), ('ttype', '!=', 'one2many'), ('ttype', '!=', 'many2many')]"
|
||||
on_change="onchange_sub_model_object_value_field(model_object_field)"/>
|
||||
<field name="sub_object" readonly="1" attrs="{'invisible': [('model_id', '=', False)]}"/>
|
||||
<field name="sub_model_object_field"
|
||||
domain="[('model_id', '=', sub_object), ('ttype', '!=', 'one2many'), ('ttype', '!=', 'many2many')]"
|
||||
attrs="{'readonly':[('sub_object','=',False)],
|
||||
'required':[('sub_object','!=',False)],
|
||||
'invisible': [('model_id', '=', False)]}"
|
||||
on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field)"/>
|
||||
<field name="copyvalue" attrs="{'invisible': [('model_id', '=', False)]}"/>
|
||||
<h3 colspan="2">Find the ID of a record in the database</h3>
|
||||
<field name="id_object" on_change="onchange_id_object(id_object)"/>
|
||||
<field name="id_value" />
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -233,10 +233,10 @@ class ir_attachment(osv.osv):
|
|||
targets = cr.dictfetchall()
|
||||
model_attachments = {}
|
||||
for target_dict in targets:
|
||||
if not (target_dict['res_id'] and target_dict['res_model']):
|
||||
if not target_dict['res_model']:
|
||||
continue
|
||||
# model_attachments = { 'model': { 'res_id': [id1,id2] } }
|
||||
model_attachments.setdefault(target_dict['res_model'],{}).setdefault(target_dict['res_id'],set()).add(target_dict['id'])
|
||||
model_attachments.setdefault(target_dict['res_model'],{}).setdefault(target_dict['res_id'] or 0, set()).add(target_dict['id'])
|
||||
|
||||
# To avoid multiple queries for each attachment found, checks are
|
||||
# performed in batch as much as possible.
|
||||
|
@ -250,7 +250,7 @@ class ir_attachment(osv.osv):
|
|||
|
||||
# filter ids according to what access rules permit
|
||||
target_ids = targets.keys()
|
||||
allowed_ids = self.pool[model].search(cr, uid, [('id', 'in', target_ids)], context=context)
|
||||
allowed_ids = [0] + self.pool[model].search(cr, uid, [('id', 'in', target_ids)], context=context)
|
||||
disallowed_ids = set(target_ids).difference(allowed_ids)
|
||||
for res_id in disallowed_ids:
|
||||
for attach_id in targets[res_id]:
|
||||
|
|
|
@ -70,7 +70,8 @@
|
|||
<field name="name">ir.module.module.form</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
<field name="arch" type="xml">
|
||||
<form create="0" edit="0" string="Module" version="7.0" class='oe_styling_v8'>
|
||||
<form create="0" edit="0" string="Module" version="7.0">
|
||||
<link rel="stylesheet" href="/base/static/src/css/description.css"></link>
|
||||
<sheet>
|
||||
<field name="icon_image" widget="image" class="oe_avatar oe_left"/>
|
||||
<div class="oe_title">
|
||||
|
@ -127,9 +128,8 @@
|
|||
<field name="reports_by_module"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<field name="description_html" class='oe_styling_v8'/>
|
||||
</sheet>
|
||||
<field name="description_html" class='oe_app_description oe_styling_v8'/>
|
||||
<button name="button_immediate_install" states="uninstalled" string="Install" type="object" class="oe_highlight oe_center"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
sass:
|
||||
sass -t expanded --compass --watch --unix-newlines *.sass
|
|
@ -0,0 +1,732 @@
|
|||
@charset "utf-8";
|
||||
/*
|
||||
* This CSS is for the html description of modules
|
||||
* TODO clean
|
||||
*/
|
||||
/* --------------------------------- *
|
||||
* STYLING CONTEXT *
|
||||
* --------------------------------- */
|
||||
/* --- Styling for the V8/Lato/White/Purple design --- */
|
||||
.openerp .oe_form_sheet_width {
|
||||
max-width: 960px;
|
||||
}
|
||||
|
||||
.openerp .oe_form .oe_styling_v8 {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Open Sans", "Helvetica", Sans;
|
||||
font-weight: 300;
|
||||
color: #646464;
|
||||
background: white;
|
||||
font-size: 16px;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 .oe_websiteonly {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 .oe_website_contents {
|
||||
background: whitesmoke;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 b {
|
||||
font-weight: 600;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 a {
|
||||
color: #6d57e0;
|
||||
text-decoration: none;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 a:visited {
|
||||
color: #5b284f;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 a:hover {
|
||||
color: #0096eb;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 .oe_title_font {
|
||||
font-family: "Lato", "Open Sans", "Helvetica", Sans;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 .oe_page {
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
-webkit-border-radius: 1px;
|
||||
-moz-border-radius: 1px;
|
||||
-ms-border-radius: 1px;
|
||||
-o-border-radius: 1px;
|
||||
border-radius: 1px;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
|
||||
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 .oe_emph {
|
||||
font-weight: 400;
|
||||
}
|
||||
.openerp .oe_form .oe_styling_v8 .oe_dark {
|
||||
overflow: hidden;
|
||||
background: #fcfcfc;
|
||||
-webkit-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset;
|
||||
-moz-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset;
|
||||
box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset;
|
||||
}
|
||||
|
||||
/* --------------------------------- *
|
||||
* LAYOUT *
|
||||
* --------------------------------- */
|
||||
/* ------ BASE GRID CONSTRUCTS ----- */
|
||||
.oe_page {
|
||||
margin: 0px auto 64px auto;
|
||||
max-width: 992px;
|
||||
}
|
||||
|
||||
.oe_row {
|
||||
width: 928px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.oe_row.oe_fit {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.oe_clearfix:after, .oe_row:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
line-height: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
[class*='oe_span'] {
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.oe_span12 {
|
||||
width: 928px;
|
||||
}
|
||||
|
||||
.oe_span10 {
|
||||
width: 773px;
|
||||
}
|
||||
|
||||
.oe_span9 {
|
||||
width: 696px;
|
||||
}
|
||||
|
||||
.oe_span8 {
|
||||
width: 618px;
|
||||
}
|
||||
|
||||
.oe_span6 {
|
||||
width: 464px;
|
||||
}
|
||||
|
||||
.oe_span4 {
|
||||
width: 309px;
|
||||
}
|
||||
|
||||
.oe_span3 {
|
||||
width: 232px;
|
||||
}
|
||||
|
||||
.oe_span2 {
|
||||
width: 154px;
|
||||
}
|
||||
|
||||
[class*='oe_span'].oe_fit {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
[class*='oe_span'].oe_right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.oe_row.oe_flex [class*='oe_span'] {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
vertical-align: top;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
width: auto;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span12 {
|
||||
max-width: 928px;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span10 {
|
||||
max-width: 769px;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span9 {
|
||||
max-width: 692px;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span8 {
|
||||
max-width: 614px;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span6 {
|
||||
max-width: 460px;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span4 {
|
||||
max-width: 305px;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span3 {
|
||||
max-width: 228px;
|
||||
}
|
||||
.oe_row.oe_flex .oe_span2 {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.oe_mb0 {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.oe_mb4 {
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
.oe_mb8 {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.oe_mb16 {
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
|
||||
.oe_mb32 {
|
||||
margin-bottom: 32px !important;
|
||||
}
|
||||
|
||||
.oe_mb48 {
|
||||
margin-bottom: 48px !important;
|
||||
}
|
||||
|
||||
.oe_mb64 {
|
||||
margin-bottom: 64px !important;
|
||||
}
|
||||
|
||||
.oe_mt0 {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.oe_mt4 {
|
||||
margin-top: 4px !important;
|
||||
}
|
||||
|
||||
.oe_mt8 {
|
||||
margin-top: 8px !important;
|
||||
}
|
||||
|
||||
.oe_mt16 {
|
||||
margin-top: 16px !important;
|
||||
}
|
||||
|
||||
.oe_mt32 {
|
||||
margin-top: 32px !important;
|
||||
}
|
||||
|
||||
.oe_mt48 {
|
||||
margin-top: 48px !important;
|
||||
}
|
||||
|
||||
.oe_mt64 {
|
||||
margin-top: 64px !important;
|
||||
}
|
||||
|
||||
/* ------ GENERIC LAYOUT MODIFIERS ----- */
|
||||
.oe_rightfit {
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
.oe_leftfit {
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.oe_leftalign {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.oe_rightalign {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.oe_centeralign {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.oe_centered {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.oe_hidden {
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.oe_invisible {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.oe_transparent {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.oe_mb0 {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.oe_mb4 {
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
.oe_mb8 {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.oe_mb16 {
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
|
||||
.oe_mb32 {
|
||||
margin-bottom: 32px !important;
|
||||
}
|
||||
|
||||
.oe_mb64 {
|
||||
margin-bottom: 64px !important;
|
||||
}
|
||||
|
||||
.oe_spaced {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.oe_more_spaced {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 64px;
|
||||
}
|
||||
|
||||
.oe_padded {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.oe_more_padded {
|
||||
padding-top: 32px;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
/* --------------------------------- *
|
||||
* WEBPAGE COMPONENTS *
|
||||
* --------------------------------- */
|
||||
/* ------ BUTTONS ----- */
|
||||
.oe_button {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.oe_styling_v8 .oe_button, .oe_styling_v8 a.oe_button {
|
||||
padding: 8px 14px;
|
||||
background: #8b72b6;
|
||||
color: white;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-ms-border-radius: 2px;
|
||||
-o-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
-webkit-box-shadow: 0px 2px 0px #afa8cc;
|
||||
-moz-box-shadow: 0px 2px 0px #afa8cc;
|
||||
box-shadow: 0px 2px 0px #afa8cc;
|
||||
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.44);
|
||||
border: solid 1px rgba(0, 0, 0, 0.09);
|
||||
-webkit-transition-property: bottom, background;
|
||||
-moz-transition-property: bottom, background;
|
||||
-o-transition-property: bottom, background;
|
||||
transition-property: bottom, background;
|
||||
-webkit-transition-duration: 250ms;
|
||||
-moz-transition-duration: 250ms;
|
||||
-o-transition-duration: 250ms;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
.oe_styling_v8 .oe_button:hover, .oe_styling_v8 a.oe_button:hover {
|
||||
background: #8b5bdd;
|
||||
color: white;
|
||||
}
|
||||
.oe_styling_v8 .oe_button:active, .oe_styling_v8 a.oe_button:active {
|
||||
background: #333333;
|
||||
bottom: -3px;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_big, .oe_styling_v8 a.oe_button.oe_big {
|
||||
font-size: 24px;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_bigger, .oe_styling_v8 a.oe_button.oe_bigger {
|
||||
font-size: 32px;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_small, .oe_styling_v8 a.oe_button.oe_small {
|
||||
font-size: 13px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_small:active, .oe_styling_v8 a.oe_button.oe_small:active {
|
||||
bottom: -1px;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_medium, .oe_styling_v8 a.oe_button.oe_medium {
|
||||
padding: 5px 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_tacky, .oe_styling_v8 a.oe_button.oe_tacky {
|
||||
background: #ff4444;
|
||||
-webkit-box-shadow: 0px 2px 0px #eba8a8;
|
||||
-moz-box-shadow: 0px 2px 0px #eba8a8;
|
||||
box-shadow: 0px 2px 0px #eba8a8;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_tacky:hover, .oe_styling_v8 a.oe_button.oe_tacky:hover {
|
||||
background: #ff1010;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_tacky:active, .oe_styling_v8 a.oe_button.oe_tacky:active {
|
||||
background: black;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_disabled, .oe_styling_v8 a.oe_button.oe_disabled {
|
||||
background: #c8c8c8;
|
||||
-webkit-box-shadow: 0px 2px 0px #b4b4b4;
|
||||
-moz-box-shadow: 0px 2px 0px #b4b4b4;
|
||||
box-shadow: 0px 2px 0px #b4b4b4;
|
||||
cursor: default;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_disabled:hover, .oe_styling_v8 a.oe_button.oe_disabled:hover {
|
||||
background: #c8c8c8;
|
||||
-webkit-box-shadow: 0px 2px 0px #b4b4b4;
|
||||
-moz-box-shadow: 0px 2px 0px #b4b4b4;
|
||||
box-shadow: 0px 2px 0px #b4b4b4;
|
||||
}
|
||||
.oe_styling_v8 .oe_button.oe_disabled:active, .oe_styling_v8 a.oe_button.oe_disabled:active {
|
||||
background: #c8c8c8;
|
||||
bottom: 0px;
|
||||
-webkit-box-shadow: 0px 2px 0px #b4b4b4;
|
||||
-moz-box-shadow: 0px 2px 0px #b4b4b4;
|
||||
box-shadow: 0px 2px 0px #b4b4b4;
|
||||
}
|
||||
|
||||
.oe_styling_v8.oe_styling_black .oe_button {
|
||||
-webkit-box-shadow: 0px 2px 0px #463555;
|
||||
-moz-box-shadow: 0px 2px 0px #463555;
|
||||
box-shadow: 0px 2px 0px #463555;
|
||||
}
|
||||
|
||||
/* ------ FORMS ----- */
|
||||
.oe_styling_v8 {
|
||||
/* FIXME: this is a quick hack for the release */
|
||||
}
|
||||
.oe_styling_v8 .oe_input {
|
||||
padding: 4px 7px;
|
||||
border-radius: 3px;
|
||||
border: solid 1px #d6d6d6;
|
||||
box-shadow: 0px 2px #e6e6e6;
|
||||
background: #fafafa;
|
||||
font-weight: 300;
|
||||
outline: none;
|
||||
-webkit-transition: all 150ms linear;
|
||||
-moz-transition: all 150ms linear;
|
||||
-o-transition: all 150ms linear;
|
||||
transition: all 150ms linear;
|
||||
}
|
||||
.oe_styling_v8 .oe_input:focus {
|
||||
border: solid 1px #969696;
|
||||
box-shadow: 0px 2px #d2d2d2;
|
||||
}
|
||||
.oe_styling_v8 .oe_input.oe_valid {
|
||||
background: #f2ffec;
|
||||
border-color: #b1ebb6;
|
||||
box-shadow: 0px 2px #e1f8e1;
|
||||
color: #0f610f;
|
||||
}
|
||||
.oe_styling_v8 .oe_input.oe_invalid {
|
||||
background: #fff2f2;
|
||||
border-color: #ebb1b1;
|
||||
box-shadow: 0px 2px #f8e1e1;
|
||||
color: #610f0f;
|
||||
}
|
||||
.oe_styling_v8 .oe_input.oe_big {
|
||||
padding: 8px 14px;
|
||||
}
|
||||
.oe_styling_v8 .oe_input_label {
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
}
|
||||
.oe_styling_v8 .oe_input_label.oe_big {
|
||||
font-size: 20px;
|
||||
}
|
||||
.oe_styling_v8 .oe_textarea {
|
||||
width: 300px;
|
||||
height: 80px;
|
||||
}
|
||||
.oe_styling_v8 .oe_form_layout_table {
|
||||
width: 100%;
|
||||
}
|
||||
.oe_styling_v8 .oe_form_layout_table td {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.oe_styling_v8 .oe_form_layout_table td:first-child {
|
||||
text-align: right;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
/* ------ SLOGANS ----- */
|
||||
.oe_styling_v8 .oe_slogan {
|
||||
color: #333333;
|
||||
font-family: "Lato", "Open Sans", "Helvetica", Sans;
|
||||
text-align: center;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.oe_styling_v8 h1.oe_slogan {
|
||||
font-size: 64px;
|
||||
font-weight: 900;
|
||||
margin-top: 48px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
.oe_styling_v8 h2.oe_slogan {
|
||||
font-size: 40px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.oe_styling_v8 h3.oe_slogan {
|
||||
font-size: 26px;
|
||||
font-weight: 300;
|
||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
|
||||
opacity: 0.5;
|
||||
}
|
||||
.oe_styling_v8 h4.oe_slogan {
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.oe_styling_v8 h4.oe_slogan:before, .oe_styling_v8 h4.oe_slogan:after {
|
||||
margin: 0 20px;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 0px;
|
||||
border-top: solid 1px;
|
||||
vertical-align: middle;
|
||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30);
|
||||
opacity: 0.3;
|
||||
}
|
||||
.oe_styling_v8 h5.oe_slogan {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* ------ QUOTES ----- */
|
||||
.oe_quote {
|
||||
margin: 8px;
|
||||
padding: 16px;
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
border: solid 1px rgba(0, 0, 0, 0.06);
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-ms-border-radius: 2px;
|
||||
-o-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.oe_quote .oe_q, .oe_quote q {
|
||||
margin: 10px;
|
||||
display: block;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
color: #4e66e7;
|
||||
}
|
||||
.oe_quote .oe_q:before, .oe_quote .oe_q:after, .oe_quote q:before, .oe_quote q:after {
|
||||
content: '"';
|
||||
font-weight: 900;
|
||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20);
|
||||
opacity: 0.2;
|
||||
}
|
||||
.oe_quote cite {
|
||||
display: block;
|
||||
font-style: normal;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.oe_quote .oe_photo {
|
||||
float: left;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-ms-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.oe_quote .oe_author {
|
||||
font-size: 20px;
|
||||
padding-top: 6px;
|
||||
color: #8d7bac;
|
||||
}
|
||||
|
||||
.oe_dark .oe_quote {
|
||||
background: white;
|
||||
border: 1px solid #f0f0ff;
|
||||
}
|
||||
|
||||
/* ------ PICTURES ----- */
|
||||
.oe_picture {
|
||||
display: block;
|
||||
max-width: 84%;
|
||||
max-height: 400px;
|
||||
margin: 16px 8%;
|
||||
}
|
||||
|
||||
.oe_screenshot {
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-ms-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
-webkit-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.oe_pic_ctr {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.oe_pic_ctr > img.oe_picture {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.oe_pic_ctr > .oe_title {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 38px;
|
||||
}
|
||||
|
||||
.oe_styling_v8 .oe_pic_ctr > .oe_title {
|
||||
font-size: 64px;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
text-shadow: 0px 2px 0px #494949, 0px 2px 5px rgba(0, 0, 0, 0.33), 0px 0px 60px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
/* ----- Link Image with Footer ----- */
|
||||
/* FIXME: Terrible CSS, rewrite this */
|
||||
div.oe_demo {
|
||||
position: relative;
|
||||
border: 1px solid #dedede;
|
||||
}
|
||||
div.oe_demo span.oe_demo_play {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 80px;
|
||||
height: 60px;
|
||||
margin-top: -30px;
|
||||
margin-left: -40px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: url("../img/layout/play-button.png") no-repeat left top transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
div.oe_demo img {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
div.oe_demo div.oe_demo_footer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
opacity: 0.85;
|
||||
bottom: -1px;
|
||||
width: 100%;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
div.oe_demo:hover span.oe_demo_play {
|
||||
background: url("../img/layout/play-button-over.png") no-repeat left top transparent;
|
||||
}
|
||||
|
||||
/* ----- SEPARATOR ----- */
|
||||
.oe_styling_v8 .oe_container.oe_separator {
|
||||
height: 64px;
|
||||
margin-bottom: 16px;
|
||||
background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0, 0, 0, 0)), color-stop(100%, rgba(0, 0, 0, 0.02)));
|
||||
background: -webkit-linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.02));
|
||||
background: -moz-linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.02));
|
||||
background: -o-linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.02));
|
||||
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.02));
|
||||
-webkit-box-shadow: 0px -3px 10px -5px rgba(0, 0, 0, 0.1) inset;
|
||||
-moz-box-shadow: 0px -3px 10px -5px rgba(0, 0, 0, 0.1) inset;
|
||||
box-shadow: 0px -3px 10px -5px rgba(0, 0, 0, 0.1) inset;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
/* ----- TABS ----- */
|
||||
.oe_row_tabs {
|
||||
text-align: center;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding-top: 21px;
|
||||
}
|
||||
|
||||
.oe_row_tab {
|
||||
position: relative;
|
||||
min-width: 120px;
|
||||
padding: 8px;
|
||||
font-size: 20px;
|
||||
display: inline-block;
|
||||
margin: 0px -2px;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border: solid 1px rgba(0, 0, 0, 0.1);
|
||||
border-bottom: none;
|
||||
background: #fafafa;
|
||||
background-image: +linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.02));
|
||||
box-shadow: 0px -3px 10px -5px rgba(0, 0, 0, 0.1) inset;
|
||||
cursor: pointer;
|
||||
-webkit-transition: all 250ms linear;
|
||||
-moz-transition: all 250ms linear;
|
||||
-o-transition: all 250ms linear;
|
||||
transition: all 250ms linear;
|
||||
}
|
||||
|
||||
.oe_row_tab:hover {
|
||||
padding-bottom: 12px;
|
||||
top: -4px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.oe_row_tab.oe_active {
|
||||
background-color: white;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
border-top-color: #8272b6;
|
||||
border-top-width: 2px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.oe_row_tab.oe_active:hover {
|
||||
padding-bottom: 8px;
|
||||
top: 0asx;
|
||||
}
|
||||
|
||||
/* ------ CALL TO ACTION ----- */
|
||||
.oe_calltoaction {
|
||||
height: 32px;
|
||||
margin-top: -32px;
|
||||
position: relative;
|
||||
}
|
|
@ -0,0 +1,572 @@
|
|||
@charset "utf-8"
|
||||
@import "compass/css3"
|
||||
@import "compass/css3/user-interface"
|
||||
|
||||
/**
|
||||
* This CSS is for the html description of modules
|
||||
* TODO clean
|
||||
*/
|
||||
|
||||
/* --------------------------------- *
|
||||
* STYLING CONTEXT *
|
||||
* --------------------------------- */
|
||||
|
||||
/* --- Styling for the V8/Lato/White/Purple design --- */
|
||||
|
||||
$v8_title_font_color: rgb(51,51,51)
|
||||
$v8_text_font_color: rgb(100,100,100)
|
||||
$v8_font_paragraph_color: rgb(51,51,51)
|
||||
$v8_body_color: rgb(245,245,245)
|
||||
$v8_bg_color: rgb(255,255,255)
|
||||
$v8_text_font_family: 'Open Sans','Helvetica',Sans
|
||||
$v8_title_font_family: 'Lato','Open Sans','Helvetica',Sans
|
||||
$v8_anchor_color: #6D57E0
|
||||
$v8_anchor_visited_color: rgb(91, 40, 79)
|
||||
|
||||
|
||||
.openerp .oe_form_sheet_width
|
||||
max-width: 960px
|
||||
|
||||
.openerp .oe_form .oe_styling_v8
|
||||
width: 100%
|
||||
padding: 0
|
||||
margin: 0
|
||||
font-family: $v8_text_font_family
|
||||
font-weight: 300
|
||||
color: $v8_text_font_color
|
||||
background: $v8_bg_color
|
||||
font-size: 16px
|
||||
|
||||
.oe_websiteonly
|
||||
display: none
|
||||
|
||||
.oe_website_contents
|
||||
background: $v8_body_color
|
||||
padding-bottom: 1px
|
||||
|
||||
b
|
||||
font-weight: 600
|
||||
a
|
||||
color: $v8_anchor_color
|
||||
text-decoration: none
|
||||
a:visited
|
||||
color: $v8_anchor_visited_color
|
||||
a:hover
|
||||
color: #0096EB
|
||||
|
||||
.oe_title_font
|
||||
font-family: $v8_title_font_family
|
||||
|
||||
|
||||
.oe_page
|
||||
background: $v8_bg_color
|
||||
overflow: hidden
|
||||
+border-radius(1px)
|
||||
+box-shadow(0 1px 3px rgba(0,0,0,0.35))
|
||||
|
||||
.oe_emph
|
||||
font-weight: 400
|
||||
|
||||
.oe_dark
|
||||
overflow: hidden
|
||||
background: #FCFCFC
|
||||
+box-shadow(0px 5px 9px -7px rgba(0,0,255,0.5) inset, 0px -3px 9px -7px rgba(0,0,255,0.5) inset)
|
||||
|
||||
/* --------------------------------- *
|
||||
* LAYOUT *
|
||||
* --------------------------------- */
|
||||
|
||||
/* ------ BASE GRID CONSTRUCTS ----- */
|
||||
|
||||
.oe_page
|
||||
margin: 0px auto 64px auto
|
||||
max-width: 992px
|
||||
|
||||
|
||||
.oe_row
|
||||
width: 928px
|
||||
margin-top: 16px
|
||||
margin-bottom: 16px
|
||||
margin-left: auto
|
||||
margin-right: auto
|
||||
|
||||
.oe_row.oe_fit
|
||||
width: auto
|
||||
|
||||
.oe_clearfix:after, .oe_row:after
|
||||
content: "."
|
||||
display: block
|
||||
clear: both
|
||||
visibility: hidden
|
||||
line-height: 0
|
||||
height: 0
|
||||
|
||||
$oe_span12_width: 928px
|
||||
$oe_span10_width: 773px
|
||||
$oe_span9_width: 696px
|
||||
$oe_span8_width: 618px
|
||||
$oe_span6_width: 464px
|
||||
$oe_span4_width: 309px
|
||||
$oe_span3_width: 232px
|
||||
$oe_span2_width: 154px
|
||||
|
||||
[class*='oe_span']
|
||||
float: left
|
||||
+box-sizing(border-box)
|
||||
padding: 0 16px
|
||||
.oe_span12
|
||||
width: $oe_span12_width
|
||||
.oe_span10
|
||||
width: $oe_span10_width
|
||||
.oe_span9
|
||||
width: $oe_span9_width
|
||||
.oe_span8
|
||||
width: $oe_span8_width
|
||||
.oe_span6
|
||||
width: $oe_span6_width
|
||||
.oe_span4
|
||||
width: $oe_span4_width
|
||||
.oe_span3
|
||||
width: $oe_span3_width
|
||||
.oe_span2
|
||||
width: $oe_span2_width
|
||||
|
||||
[class*='oe_span'].oe_fit
|
||||
padding-left: 0px !important
|
||||
padding-right: 0px !important
|
||||
|
||||
[class*='oe_span'].oe_right
|
||||
float: right
|
||||
|
||||
.oe_row.oe_flex
|
||||
[class*='oe_span']
|
||||
display: inline-block
|
||||
float: none
|
||||
vertical-align: top
|
||||
+box-sizing(border-box)
|
||||
padding: 0 16px
|
||||
width: auto
|
||||
.oe_span12
|
||||
max-width: $oe_span12_width
|
||||
.oe_span10
|
||||
max-width: ($oe_span10_width +-4px)
|
||||
.oe_span9
|
||||
max-width: ($oe_span9_width +-4px)
|
||||
.oe_span8
|
||||
max-width: ($oe_span8_width +-4px)
|
||||
.oe_span6
|
||||
max-width: ($oe_span6_width +-4px)
|
||||
.oe_span4
|
||||
max-width: ($oe_span4_width +-4px)
|
||||
.oe_span3
|
||||
max-width: ($oe_span3_width +-4px)
|
||||
.oe_span2
|
||||
max-width: ($oe_span2_width +-4px)
|
||||
|
||||
.oe_mb0
|
||||
margin-bottom: 0px !important
|
||||
.oe_mb4
|
||||
margin-bottom: 4px !important
|
||||
.oe_mb8
|
||||
margin-bottom: 8px !important
|
||||
.oe_mb16
|
||||
margin-bottom: 16px !important
|
||||
.oe_mb32
|
||||
margin-bottom: 32px !important
|
||||
.oe_mb48
|
||||
margin-bottom: 48px !important
|
||||
.oe_mb64
|
||||
margin-bottom: 64px !important
|
||||
|
||||
.oe_mt0
|
||||
margin-top: 0px !important
|
||||
.oe_mt4
|
||||
margin-top: 4px !important
|
||||
.oe_mt8
|
||||
margin-top: 8px !important
|
||||
.oe_mt16
|
||||
margin-top: 16px !important
|
||||
.oe_mt32
|
||||
margin-top: 32px !important
|
||||
.oe_mt48
|
||||
margin-top: 48px !important
|
||||
.oe_mt64
|
||||
margin-top: 64px !important
|
||||
|
||||
/* ------ GENERIC LAYOUT MODIFIERS ----- */
|
||||
|
||||
.oe_rightfit
|
||||
padding-right: 0px !important
|
||||
.oe_leftfit
|
||||
padding-left: 0px !important
|
||||
.oe_leftalign
|
||||
text-align: left
|
||||
.oe_rightalign
|
||||
text-align: right
|
||||
.oe_centeralign
|
||||
text-align: center
|
||||
.oe_centered
|
||||
margin-left: auto
|
||||
margin-right: auto
|
||||
.oe_hidden
|
||||
display: none !important
|
||||
opacity: 0 !important
|
||||
.oe_invisible
|
||||
visibility: hidden !important
|
||||
.oe_transparent
|
||||
opacity: 0 !important
|
||||
|
||||
.oe_mb0
|
||||
margin-bottom: 0px !important
|
||||
.oe_mb4
|
||||
margin-bottom: 4px !important
|
||||
.oe_mb8
|
||||
margin-bottom: 8px !important
|
||||
.oe_mb16
|
||||
margin-bottom: 16px !important
|
||||
.oe_mb32
|
||||
margin-bottom: 32px !important
|
||||
.oe_mb64
|
||||
margin-bottom: 64px !important
|
||||
|
||||
.oe_spaced
|
||||
margin-top: 32px
|
||||
margin-bottom: 32px
|
||||
.oe_more_spaced
|
||||
margin-top: 64px
|
||||
margin-bottom: 64px
|
||||
.oe_padded
|
||||
padding-top: 16px
|
||||
padding-bottom: 16px
|
||||
.oe_more_padded
|
||||
padding-top: 32px
|
||||
padding-bottom: 32px
|
||||
|
||||
/* --------------------------------- *
|
||||
* WEBPAGE COMPONENTS *
|
||||
* --------------------------------- */
|
||||
|
||||
/* ------ BUTTONS ----- */
|
||||
|
||||
.oe_button
|
||||
position: relative
|
||||
bottom: 0
|
||||
display: inline-block
|
||||
cursor: pointer
|
||||
+user-select(none)
|
||||
|
||||
.oe_styling_v8 .oe_button, .oe_styling_v8 a.oe_button
|
||||
padding: 8px 14px
|
||||
background: rgb(139, 114, 182)
|
||||
color: white
|
||||
+border-radius(2px)
|
||||
+box-shadow(0px 2px 0px rgb(175, 168, 204))
|
||||
+text-shadow(0px 1px 1px rgba(0,0,0, 0.44))
|
||||
border: solid 1px rgba(0,0,0,0.09)
|
||||
+transition-property((bottom, background))
|
||||
+transition-duration(250ms)
|
||||
&:hover
|
||||
background: rgb(139,91,221)
|
||||
color: white
|
||||
&:active
|
||||
background: rgb(51,51,51)
|
||||
bottom: -3px
|
||||
&.oe_big
|
||||
font-size: 24px
|
||||
&.oe_bigger
|
||||
font-size: 32px
|
||||
&.oe_small
|
||||
font-size: 13px
|
||||
padding: 2px 4px
|
||||
&:active
|
||||
bottom: -1px
|
||||
&.oe_medium
|
||||
padding: 5px 12px
|
||||
font-size: 16px
|
||||
&.oe_tacky
|
||||
background: rgb(255,68,68)
|
||||
+box-shadow(0px 2px 0px #eba8a8)
|
||||
&:hover
|
||||
background: rgb(255,16,16)
|
||||
&:active
|
||||
background: black
|
||||
&.oe_disabled
|
||||
background: rgb(200,200,200)
|
||||
+box-shadow(0px 2px 0px rgb(180,180,180))
|
||||
cursor: default
|
||||
&:hover
|
||||
background: rgb(200,200,200)
|
||||
+box-shadow(0px 2px 0px rgb(180,180,180))
|
||||
&:active
|
||||
background: rgb(200,200,200)
|
||||
bottom: 0px
|
||||
+box-shadow(0px 2px 0px rgb(180,180,180))
|
||||
|
||||
.oe_styling_v8.oe_styling_black .oe_button
|
||||
+box-shadow(0px 2px 0px rgb(70,53,85))
|
||||
|
||||
/* ------ FORMS ----- */
|
||||
|
||||
.oe_styling_v8
|
||||
.oe_input
|
||||
padding: 4px 7px
|
||||
border-radius: 3px
|
||||
border: solid 1px rgb(214,214,214)
|
||||
box-shadow: 0px 2px rgb(230,230,230)
|
||||
background: rgb(250,250,250)
|
||||
font-weight: 300
|
||||
outline: none
|
||||
@include transition( all 150ms linear )
|
||||
&:focus
|
||||
border: solid 1px rgb(150,150,150)
|
||||
box-shadow: 0px 2px rgb(210,210,210)
|
||||
|
||||
&.oe_valid
|
||||
background: #F2FFEC
|
||||
border-color: rgb(177,235,182)
|
||||
box-shadow: 0px 2px rgb(225,248,225)
|
||||
color: rgb(15,97,15)
|
||||
|
||||
&.oe_invalid
|
||||
background: rgb(255,242,242)
|
||||
border-color: #EBB1B1
|
||||
box-shadow: 0px 2px #F8E1E1
|
||||
color: #610F0F
|
||||
&.oe_big
|
||||
padding: 8px 14px
|
||||
.oe_input_label
|
||||
font-weight: 300
|
||||
font-size: 16px
|
||||
&.oe_big
|
||||
font-size: 20px
|
||||
|
||||
/* FIXME: this is a quick hack for the release */
|
||||
|
||||
.oe_textarea
|
||||
width: 300px
|
||||
height: 80px
|
||||
.oe_form_layout_table
|
||||
width: 100%
|
||||
td
|
||||
padding-bottom: 16px
|
||||
&:first-child
|
||||
text-align: right
|
||||
padding-right: 16px
|
||||
|
||||
/* ------ SLOGANS ----- */
|
||||
|
||||
.oe_styling_v8
|
||||
.oe_slogan
|
||||
color: $v8_title_font_color
|
||||
font-family: $v8_title_font_family
|
||||
text-align: center
|
||||
margin-top: 32px
|
||||
margin-bottom: 32px
|
||||
|
||||
h1.oe_slogan
|
||||
font-size: 64px
|
||||
font-weight: 900
|
||||
margin-top: 48px
|
||||
margin-bottom: 48px
|
||||
|
||||
h2.oe_slogan
|
||||
font-size: 40px
|
||||
font-weight: 300
|
||||
|
||||
h3.oe_slogan
|
||||
font-size: 26px
|
||||
font-weight: 300
|
||||
+opacity(0.5)
|
||||
|
||||
h4.oe_slogan
|
||||
font-size: 24px
|
||||
font-weight: 300
|
||||
h4.oe_slogan:before, h4.oe_slogan:after
|
||||
margin: 0 20px
|
||||
content: ""
|
||||
display: inline-block
|
||||
width: 100px
|
||||
height: 0px
|
||||
border-top: solid 1px
|
||||
vertical-align: middle
|
||||
+opacity(0.3)
|
||||
|
||||
h5.oe_slogan
|
||||
font-weight: 300
|
||||
//TODO
|
||||
|
||||
/* ------ QUOTES ----- */
|
||||
|
||||
.oe_quote
|
||||
margin: 8px
|
||||
padding: 16px
|
||||
background: rgba(0,0,0,0.02)
|
||||
border: solid 1px rgba(0,0,0,0.06)
|
||||
+border-radius(2px)
|
||||
|
||||
.oe_q,q
|
||||
margin: 10px
|
||||
display: block
|
||||
font-style: italic
|
||||
text-align: center
|
||||
font-size: 20px
|
||||
color: rgb(78, 102, 231)
|
||||
&:before, &:after
|
||||
content: '\"'
|
||||
font-weight: 900
|
||||
+opacity(0.2)
|
||||
|
||||
cite
|
||||
display: block
|
||||
font-style: normal
|
||||
margin-top: 16px
|
||||
|
||||
.oe_photo
|
||||
float: left
|
||||
+border-radius(3px)
|
||||
margin-right: 16px
|
||||
|
||||
.oe_author
|
||||
font-size: 20px
|
||||
padding-top: 6px
|
||||
color: rgb(141, 123, 172)
|
||||
|
||||
.oe_dark .oe_quote
|
||||
background: white
|
||||
border: 1px solid rgb(240,240,255)
|
||||
|
||||
/* ------ PICTURES ----- */
|
||||
|
||||
// display a picture in a span
|
||||
.oe_picture
|
||||
display: block
|
||||
max-width: 84%
|
||||
max-height: 400px
|
||||
margin: 16px 8%
|
||||
|
||||
// style the picture like a screenshot
|
||||
.oe_screenshot
|
||||
+border-radius(3px)
|
||||
+box-shadow(0px 3px 8px rgba(0,0,0,0.2))
|
||||
|
||||
// display a picture taking full width of a row
|
||||
.oe_pic_ctr
|
||||
position: relative
|
||||
|
||||
.oe_pic_ctr > img.oe_picture
|
||||
width: 100%
|
||||
max-width: none
|
||||
max-height: none
|
||||
margin: 0
|
||||
|
||||
// styling of the picture's title
|
||||
.oe_pic_ctr > .oe_title
|
||||
position: absolute
|
||||
top: 15px
|
||||
right: 38px
|
||||
|
||||
.oe_styling_v8 .oe_pic_ctr > .oe_title
|
||||
font-size: 64px
|
||||
color: white
|
||||
font-weight: 600
|
||||
margin: 0
|
||||
+text-shadow( 0px 2px 0px rgb(73, 73, 73), 0px 2px 5px rgba(0, 0, 0, 0.33), 0px 0px 60px rgba(0, 0, 0, 0.22))
|
||||
|
||||
/* ----- Link Image with Footer ----- */
|
||||
/* FIXME: Terrible CSS, rewrite this */
|
||||
|
||||
div.oe_demo
|
||||
position: relative
|
||||
border: 1px solid #dedede
|
||||
span.oe_demo_play
|
||||
top: 50%
|
||||
left: 50%
|
||||
width: 80px
|
||||
height: 60px
|
||||
margin-top: -30px
|
||||
margin-left: -40px
|
||||
display: block
|
||||
position: absolute
|
||||
background: url("../img/layout/play-button.png") no-repeat left top transparent
|
||||
pointer-events: none
|
||||
img
|
||||
max-width: 100%
|
||||
width: 100%
|
||||
div.oe_demo_footer
|
||||
position: absolute
|
||||
left: 0
|
||||
background-color: rgba(0,0,0,0.4)
|
||||
opacity: 0.85
|
||||
bottom: -1px
|
||||
width: 100%
|
||||
padding-top: 7px
|
||||
padding-bottom: 7px
|
||||
color: white
|
||||
font-size: 14px
|
||||
font-weight: bold
|
||||
border-bottom-left-radius: 3px
|
||||
border-bottom-right-radius: 3px
|
||||
pointer-events: none
|
||||
|
||||
div.oe_demo:hover
|
||||
span.oe_demo_play
|
||||
background: url("../img/layout/play-button-over.png") no-repeat left top transparent
|
||||
|
||||
/* ----- SEPARATOR ----- */
|
||||
|
||||
.oe_styling_v8 .oe_container.oe_separator
|
||||
height: 64px
|
||||
margin-bottom: 16px
|
||||
@include background(linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.02)))
|
||||
+box-shadow(0px -3px 10px -5px rgba(0,0,0,0.1) inset)
|
||||
overflow-y: hidden
|
||||
|
||||
/* ----- TABS ----- */
|
||||
|
||||
.oe_row_tabs
|
||||
text-align: center
|
||||
margin-top: 0px
|
||||
margin-bottom: 0px
|
||||
padding-top: 21px
|
||||
|
||||
.oe_row_tab
|
||||
position: relative
|
||||
min-width: 120px
|
||||
padding: 8px
|
||||
font-size: 20px
|
||||
display: inline-block
|
||||
margin: 0px -2px
|
||||
border-top-left-radius: 4px
|
||||
border-top-right-radius: 4px
|
||||
border: solid 1px rgba(0,0,0,0.1)
|
||||
border-bottom: none
|
||||
background: rgb(250,250,250)
|
||||
background-image: +linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0.02))
|
||||
box-shadow: 0px -3px 10px -5px rgba(0,0,0,0.1) inset
|
||||
cursor: pointer
|
||||
@include transition(all 250ms linear)
|
||||
|
||||
.oe_row_tab:hover
|
||||
padding-bottom: 12px
|
||||
top: -4px
|
||||
background-color: white
|
||||
|
||||
.oe_row_tab.oe_active
|
||||
background-color: white
|
||||
background-image: none
|
||||
box-shadow: none
|
||||
border-top-color: rgb(130, 114, 182)
|
||||
border-top-width: 2px
|
||||
cursor: default
|
||||
|
||||
.oe_row_tab.oe_active:hover
|
||||
padding-bottom: 8px
|
||||
top: 0asx
|
||||
|
||||
/* ------ CALL TO ACTION ----- */
|
||||
|
||||
.oe_calltoaction
|
||||
height: 32px
|
||||
margin-top: -32px
|
||||
position: relative
|
||||
|
|
@ -92,7 +92,8 @@ openerp.base = function(instance) {
|
|||
|
||||
start: function() {
|
||||
var self = this;
|
||||
return self.get_client().
|
||||
// desactivated for now because apps does not work anyway due to changes in the framework
|
||||
/*return self.get_client().
|
||||
done(function(client) {
|
||||
client.replace(self.$el).
|
||||
done(function() {
|
||||
|
@ -100,13 +101,13 @@ openerp.base = function(instance) {
|
|||
client.do_action(self.remote_action_id, {hide_breadcrumb: true});
|
||||
});
|
||||
}).
|
||||
fail(function(client) {
|
||||
fail(function(client) {*/
|
||||
self.do_warn(_t('OpenERP Apps Unreachable'), _t('Showing locally available modules'), true);
|
||||
self.rpc('/web/action/load', {action_id: self.failback_action_id}).done(function(action) {
|
||||
self.do_action(action);
|
||||
instance.webclient.menu.open_action(action.id);
|
||||
});
|
||||
});
|
||||
//});
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import test_base
|
||||
import test_expression
|
||||
import test_ir_actions
|
||||
import test_ir_attachment
|
||||
import test_ir_values
|
||||
import test_menu
|
||||
|
@ -10,6 +11,7 @@ import test_search
|
|||
checks = [
|
||||
test_base,
|
||||
test_expression,
|
||||
test_ir_actions,
|
||||
test_ir_attachment,
|
||||
test_ir_values,
|
||||
test_menu,
|
||||
|
|
|
@ -0,0 +1,398 @@
|
|||
import unittest2
|
||||
|
||||
from openerp.osv.orm import except_orm
|
||||
import openerp.tests.common as common
|
||||
from openerp.tools import mute_logger
|
||||
|
||||
|
||||
class TestServerActionsBase(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestServerActionsBase, self).setUp()
|
||||
cr, uid = self.cr, self.uid
|
||||
|
||||
# Models
|
||||
self.ir_actions_server = self.registry('ir.actions.server')
|
||||
self.ir_actions_client = self.registry('ir.actions.client')
|
||||
self.ir_values = self.registry('ir.values')
|
||||
self.ir_model = self.registry('ir.model')
|
||||
self.ir_model_fields = self.registry('ir.model.fields')
|
||||
self.res_partner = self.registry('res.partner')
|
||||
self.res_country = self.registry('res.country')
|
||||
|
||||
# Data on which we will run the server action
|
||||
self.test_country_id = self.res_country.create(cr, uid, {
|
||||
'name': 'TestingCountry',
|
||||
'code': 'TY',
|
||||
'address_format': 'SuperFormat',
|
||||
})
|
||||
self.test_country = self.res_country.browse(cr, uid, self.test_country_id)
|
||||
self.test_partner_id = self.res_partner.create(cr, uid, {
|
||||
'name': 'TestingPartner',
|
||||
'city': 'OrigCity',
|
||||
'country_id': self.test_country_id,
|
||||
})
|
||||
self.test_partner = self.res_partner.browse(cr, uid, self.test_partner_id)
|
||||
self.context = {
|
||||
'active_id': self.test_partner_id,
|
||||
'active_model': 'res.partner',
|
||||
}
|
||||
|
||||
# Model data
|
||||
self.res_partner_model_id = self.ir_model.search(cr, uid, [('model', '=', 'res.partner')])[0]
|
||||
self.res_partner_name_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'name')])[0]
|
||||
self.res_partner_city_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'city')])[0]
|
||||
self.res_partner_country_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'country_id')])[0]
|
||||
self.res_partner_parent_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'parent_id')])[0]
|
||||
self.res_country_model_id = self.ir_model.search(cr, uid, [('model', '=', 'res.country')])[0]
|
||||
self.res_country_name_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.country'), ('name', '=', 'name')])[0]
|
||||
self.res_country_code_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.country'), ('name', '=', 'code')])[0]
|
||||
|
||||
# create server action to
|
||||
self.act_id = self.ir_actions_server.create(cr, uid, {
|
||||
'name': 'TestAction',
|
||||
'condition': 'True',
|
||||
'model_id': self.res_partner_model_id,
|
||||
'state': 'code',
|
||||
'code': 'obj.write({"comment": "MyComment"})',
|
||||
})
|
||||
|
||||
|
||||
class TestServerActions(TestServerActionsBase):
|
||||
|
||||
def test_00_action(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
|
||||
# Do: eval 'True' condition
|
||||
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
|
||||
self.test_partner.refresh()
|
||||
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: invalid condition check')
|
||||
self.test_partner.write({'comment': False})
|
||||
|
||||
# Do: eval False condition, that should be considered as True (void = True)
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {'condition': False})
|
||||
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
|
||||
self.test_partner.refresh()
|
||||
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: invalid condition check')
|
||||
|
||||
# Do: create contextual action
|
||||
self.ir_actions_server.create_action(cr, uid, [self.act_id])
|
||||
|
||||
# Test: ir_values created
|
||||
ir_values_ids = self.ir_values.search(cr, uid, [('name', '=', 'Run TestAction')])
|
||||
self.assertEqual(len(ir_values_ids), 1, 'ir_actions_server: create_action should have created an entry in ir_values')
|
||||
ir_value = self.ir_values.browse(cr, uid, ir_values_ids[0])
|
||||
self.assertEqual(ir_value.value, 'ir.actions.server,%s' % self.act_id, 'ir_actions_server: created ir_values should reference the server action')
|
||||
self.assertEqual(ir_value.model, 'res.partner', 'ir_actions_server: created ir_values should be linked to the action base model')
|
||||
|
||||
# Do: remove contextual action
|
||||
self.ir_actions_server.unlink_action(cr, uid, [self.act_id])
|
||||
|
||||
# Test: ir_values removed
|
||||
ir_values_ids = self.ir_values.search(cr, uid, [('name', '=', 'Run TestAction')])
|
||||
self.assertEqual(len(ir_values_ids), 0, 'ir_actions_server: unlink_action should remove the ir_values record')
|
||||
|
||||
def test_10_code(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
self.ir_actions_server.write(cr, uid, self.act_id, {
|
||||
'state': 'code',
|
||||
'code': """partner_name = obj.name + '_code'
|
||||
self.pool["res.partner"].create(cr, uid, {"name": partner_name}, context=context)"""
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: code server action correctly finished should return False')
|
||||
|
||||
pids = self.res_partner.search(cr, uid, [('name', 'ilike', 'TestingPartner_code')])
|
||||
self.assertEqual(len(pids), 1, 'ir_actions_server: 1 new partner should have been created')
|
||||
|
||||
def test_20_trigger(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
|
||||
# Data: code server action (at this point code-based actions should work)
|
||||
act_id2 = self.ir_actions_server.create(cr, uid, {
|
||||
'name': 'TestAction2',
|
||||
'type': 'ir.actions.server',
|
||||
'condition': 'True',
|
||||
'model_id': self.res_partner_model_id,
|
||||
'state': 'code',
|
||||
'code': 'obj.write({"comment": "MyComment"})',
|
||||
})
|
||||
act_id3 = self.ir_actions_server.create(cr, uid, {
|
||||
'name': 'TestAction3',
|
||||
'type': 'ir.actions.server',
|
||||
'condition': 'True',
|
||||
'model_id': self.res_country_model_id,
|
||||
'state': 'code',
|
||||
'code': 'obj.write({"code": "ZZ"})',
|
||||
})
|
||||
|
||||
# Data: create workflows
|
||||
partner_wf_id = self.registry('workflow').create(cr, uid, {
|
||||
'name': 'TestWorkflow',
|
||||
'osv': 'res.partner',
|
||||
'on_create': True,
|
||||
})
|
||||
partner_act1_id = self.registry('workflow.activity').create(cr, uid, {
|
||||
'name': 'PartnerStart',
|
||||
'wkf_id': partner_wf_id,
|
||||
'flow_start': True
|
||||
})
|
||||
partner_act2_id = self.registry('workflow.activity').create(cr, uid, {
|
||||
'name': 'PartnerTwo',
|
||||
'wkf_id': partner_wf_id,
|
||||
'kind': 'function',
|
||||
'action': 'True',
|
||||
'action_id': act_id2,
|
||||
})
|
||||
partner_trs1_id = self.registry('workflow.transition').create(cr, uid, {
|
||||
'signal': 'partner_trans',
|
||||
'act_from': partner_act1_id,
|
||||
'act_to': partner_act2_id
|
||||
})
|
||||
country_wf_id = self.registry('workflow').create(cr, uid, {
|
||||
'name': 'TestWorkflow',
|
||||
'osv': 'res.country',
|
||||
'on_create': True,
|
||||
})
|
||||
country_act1_id = self.registry('workflow.activity').create(cr, uid, {
|
||||
'name': 'CountryStart',
|
||||
'wkf_id': country_wf_id,
|
||||
'flow_start': True
|
||||
})
|
||||
country_act2_id = self.registry('workflow.activity').create(cr, uid, {
|
||||
'name': 'CountryTwo',
|
||||
'wkf_id': country_wf_id,
|
||||
'kind': 'function',
|
||||
'action': 'True',
|
||||
'action_id': act_id3,
|
||||
})
|
||||
country_trs1_id = self.registry('workflow.transition').create(cr, uid, {
|
||||
'signal': 'country_trans',
|
||||
'act_from': country_act1_id,
|
||||
'act_to': country_act2_id
|
||||
})
|
||||
|
||||
# Data: re-create country and partner to benefit from the workflows
|
||||
self.test_country_id = self.res_country.create(cr, uid, {
|
||||
'name': 'TestingCountry2',
|
||||
'code': 'T2',
|
||||
})
|
||||
self.test_country = self.res_country.browse(cr, uid, self.test_country_id)
|
||||
self.test_partner_id = self.res_partner.create(cr, uid, {
|
||||
'name': 'TestingPartner2',
|
||||
'country_id': self.test_country_id,
|
||||
})
|
||||
self.test_partner = self.res_partner.browse(cr, uid, self.test_partner_id)
|
||||
self.context = {
|
||||
'active_id': self.test_partner_id,
|
||||
'active_model': 'res.partner',
|
||||
}
|
||||
|
||||
# Run the action on partner object itself ('base')
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'trigger',
|
||||
'use_relational_model': 'base',
|
||||
'wkf_model_id': self.res_partner_model_id,
|
||||
'wkf_model_name': 'res.partner',
|
||||
'wkf_transition_id': partner_trs1_id,
|
||||
})
|
||||
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
|
||||
self.test_partner.refresh()
|
||||
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: incorrect signal trigger')
|
||||
|
||||
# Run the action on related country object ('relational')
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'use_relational_model': 'relational',
|
||||
'wkf_model_id': self.res_country_model_id,
|
||||
'wkf_model_name': 'res.country',
|
||||
'wkf_field_id': self.res_partner_country_field_id,
|
||||
'wkf_transition_id': country_trs1_id,
|
||||
})
|
||||
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
|
||||
self.test_country.refresh()
|
||||
self.assertEqual(self.test_country.code, 'ZZ', 'ir_actions_server: incorrect signal trigger')
|
||||
|
||||
# Clear workflow cache, otherwise openerp will try to create workflows even if it has been deleted
|
||||
from openerp.workflow import clear_cache
|
||||
clear_cache(cr, uid)
|
||||
|
||||
def test_30_client(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
client_action_id = self.registry('ir.actions.client').create(cr, uid, {
|
||||
'name': 'TestAction2',
|
||||
'tag': 'Test',
|
||||
})
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'client_action',
|
||||
'action_id': client_action_id,
|
||||
})
|
||||
res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertEqual(res['name'], 'TestAction2', 'ir_actions_server: incorrect return result for a client action')
|
||||
|
||||
def test_40_crud_create(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
_city = 'TestCity'
|
||||
_name = 'TestNew'
|
||||
|
||||
# Do: create a new record in the same model and link it
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'object_create',
|
||||
'use_create': 'new',
|
||||
'link_new_record': True,
|
||||
'link_field_id': self.res_partner_parent_field_id,
|
||||
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field_id, 'value': _name}),
|
||||
(0, 0, {'col1': self.res_partner_city_field_id, 'value': _city})],
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new partner created
|
||||
pids = self.res_partner.search(cr, uid, [('name', 'ilike', _name)])
|
||||
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
|
||||
partner = self.res_partner.browse(cr, uid, pids[0])
|
||||
self.assertEqual(partner.city, _city, 'ir_actions_server: TODO')
|
||||
# Test: new partner linked
|
||||
self.test_partner.refresh()
|
||||
self.assertEqual(self.test_partner.parent_id.id, pids[0], 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy current record
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'object_create',
|
||||
'use_create': 'copy_current',
|
||||
'link_new_record': False,
|
||||
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field_id, 'value': 'TestCopyCurrent'}),
|
||||
(0, 0, {'col1': self.res_partner_city_field_id, 'value': 'TestCity'})],
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new partner created
|
||||
pids = self.res_partner.search(cr, uid, [('name', 'ilike', 'TestingPartner (copy)')]) # currently res_partner overrides default['name'] whatever its value
|
||||
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
|
||||
partner = self.res_partner.browse(cr, uid, pids[0])
|
||||
self.assertEqual(partner.city, 'TestCity', 'ir_actions_server: TODO')
|
||||
self.assertEqual(partner.country_id.id, self.test_partner.country_id.id, 'ir_actions_server: TODO')
|
||||
|
||||
# Do: create a new record in another model
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'object_create',
|
||||
'use_create': 'new_other',
|
||||
'crud_model_id': self.res_country_model_id,
|
||||
'link_new_record': False,
|
||||
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'obj.name', 'type': 'equation'}),
|
||||
(0, 0, {'col1': self.res_country_code_field_id, 'value': 'obj.name[0:2]', 'type': 'equation'})],
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new country created
|
||||
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'TestingPartner')])
|
||||
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
|
||||
country = self.res_country.browse(cr, uid, cids[0])
|
||||
self.assertEqual(country.code, 'TE', 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy a record in another model
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'object_create',
|
||||
'use_create': 'copy_other',
|
||||
'crud_model_id': self.res_country_model_id,
|
||||
'link_new_record': False,
|
||||
'ref_object': 'res.country,%s' % self.test_country_id,
|
||||
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'NewCountry', 'type': 'value'}),
|
||||
(0, 0, {'col1': self.res_country_code_field_id, 'value': 'NY', 'type': 'value'})],
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new country created
|
||||
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'NewCountry')])
|
||||
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
|
||||
country = self.res_country.browse(cr, uid, cids[0])
|
||||
self.assertEqual(country.code, 'NY', 'ir_actions_server: TODO')
|
||||
self.assertEqual(country.address_format, 'SuperFormat', 'ir_actions_server: TODO')
|
||||
|
||||
def test_50_crud_write(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
_name = 'TestNew'
|
||||
|
||||
# Do: create a new record in the same model and link it
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'object_write',
|
||||
'use_write': 'current',
|
||||
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field_id, 'value': _name})],
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new partner created
|
||||
pids = self.res_partner.search(cr, uid, [('name', 'ilike', _name)])
|
||||
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
|
||||
partner = self.res_partner.browse(cr, uid, pids[0])
|
||||
self.assertEqual(partner.city, 'OrigCity', 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy current record
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'use_write': 'other',
|
||||
'crud_model_id': self.res_country_model_id,
|
||||
'ref_object': 'res.country,%s' % self.test_country_id,
|
||||
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'obj.name', 'type': 'equation'})],
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new country created
|
||||
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'TestNew')])
|
||||
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy a record in another model
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'use_write': 'expression',
|
||||
'crud_model_id': self.res_country_model_id,
|
||||
'write_expression': 'object.country_id',
|
||||
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'NewCountry', 'type': 'value'})],
|
||||
})
|
||||
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new country created
|
||||
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'NewCountry')])
|
||||
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
|
||||
|
||||
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
|
||||
def test_60_multi(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
|
||||
# Data: 2 server actions that will be nested
|
||||
act1_id = self.ir_actions_server.create(cr, uid, {
|
||||
'name': 'Subaction1',
|
||||
'model_id': self.res_partner_model_id,
|
||||
'state': 'code',
|
||||
'code': 'action = {"type": "ir.actions.act_window"}',
|
||||
})
|
||||
# Do: create a new record in the same model and link it
|
||||
act2_id = self.ir_actions_server.create(cr, uid, {
|
||||
'name': 'Subaction2',
|
||||
'model_id': self.res_partner_model_id,
|
||||
'state': 'object_create',
|
||||
'use_create': 'copy_current',
|
||||
})
|
||||
self.ir_actions_server.write(cr, uid, [self.act_id], {
|
||||
'state': 'multi',
|
||||
'child_ids': [(6, 0, [act1_id, act2_id])],
|
||||
})
|
||||
|
||||
# Do: run the action
|
||||
res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
|
||||
|
||||
# Test: new partner created
|
||||
pids = self.res_partner.search(cr, uid, [('name', 'ilike', 'TestingPartner (copy)')]) # currently res_partner overrides default['name'] whatever its value
|
||||
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
|
||||
# Test: action returned
|
||||
self.assertEqual(res.get('type'), 'ir.actions.act_window', '')
|
||||
|
||||
# Test loops
|
||||
self.assertRaises(except_orm, self.ir_actions_server.write, cr, uid, [self.act_id], {
|
||||
'child_ids': [(6, 0, [self.act_id])]
|
||||
})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
|
@ -270,6 +270,21 @@ class float(_column):
|
|||
class date(_column):
|
||||
_type = 'date'
|
||||
|
||||
MONTHS = [
|
||||
('01', 'January'),
|
||||
('02', 'February'),
|
||||
('03', 'March'),
|
||||
('04', 'April'),
|
||||
('05', 'May'),
|
||||
('06', 'June'),
|
||||
('07', 'July'),
|
||||
('08', 'August'),
|
||||
('09', 'September'),
|
||||
('10', 'October'),
|
||||
('11', 'November'),
|
||||
('12', 'December')
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def today(*args):
|
||||
""" Returns the current date in a format fit for being a
|
||||
|
@ -319,6 +334,22 @@ class date(_column):
|
|||
|
||||
class datetime(_column):
|
||||
_type = 'datetime'
|
||||
|
||||
MONTHS = [
|
||||
('01', 'January'),
|
||||
('02', 'February'),
|
||||
('03', 'March'),
|
||||
('04', 'April'),
|
||||
('05', 'May'),
|
||||
('06', 'June'),
|
||||
('07', 'July'),
|
||||
('08', 'August'),
|
||||
('09', 'September'),
|
||||
('10', 'October'),
|
||||
('11', 'November'),
|
||||
('12', 'December')
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def now(*args):
|
||||
""" Returns the current datetime in a format fit for being a
|
||||
|
|
|
@ -5114,6 +5114,40 @@ class BaseModel(object):
|
|||
return False
|
||||
return True
|
||||
|
||||
def _check_m2m_recursion(self, cr, uid, ids, field_name):
|
||||
"""
|
||||
Verifies that there is no loop in a hierarchical structure of records,
|
||||
by following the parent relationship using the **parent** field until a loop
|
||||
is detected or until a top-level record is found.
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param ids: list of ids of records to check
|
||||
:param field_name: field to check
|
||||
:return: **True** if the operation can proceed safely, or **False** if an infinite loop is detected.
|
||||
"""
|
||||
|
||||
field = self._all_columns.get(field_name)
|
||||
field = field.column if field else None
|
||||
if not field or field._type != 'many2many' or field._obj != self._name:
|
||||
# field must be a many2many on itself
|
||||
raise ValueError('invalid field_name: %r' % (field_name,))
|
||||
|
||||
query = 'SELECT distinct "%s" FROM "%s" WHERE "%s" IN %%s' % (field._id2, field._rel, field._id1)
|
||||
ids_parent = ids[:]
|
||||
while ids_parent:
|
||||
ids_parent2 = []
|
||||
for i in range(0, len(ids_parent), cr.IN_MAX):
|
||||
j = i + cr.IN_MAX
|
||||
sub_ids_parent = ids_parent[i:j]
|
||||
cr.execute(query, (tuple(sub_ids_parent),))
|
||||
ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall())))
|
||||
ids_parent = ids_parent2
|
||||
for i in ids_parent:
|
||||
if i in ids:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_external_ids(self, cr, uid, ids, *args, **kwargs):
|
||||
"""Retrieve the External ID(s) of any database record.
|
||||
|
||||
|
|
|
@ -411,14 +411,11 @@ class WorkerCron(Worker):
|
|||
if rpc_request_flag:
|
||||
start_time = time.time()
|
||||
start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
while True:
|
||||
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
# TODO why isnt openerp.addons.base defined ?
|
||||
import openerp.addons.base as base
|
||||
acquired = base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
if not acquired:
|
||||
openerp.modules.registry.RegistryManager.delete(db_name)
|
||||
break
|
||||
|
||||
import openerp.addons.base as base
|
||||
base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
openerp.modules.registry.RegistryManager.delete(db_name)
|
||||
|
||||
# dont keep cursors in multi database mode
|
||||
if len(db_names) > 1:
|
||||
openerp.sql_db.close_db(db_name)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import models
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'test-workflow',
|
||||
'version': '0.1',
|
||||
'category': 'Tests',
|
||||
'description': """A module to play with workflows.""",
|
||||
'author': 'OpenERP SA',
|
||||
'maintainer': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'depends': ['base'],
|
||||
'data': ['data.xml'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,517 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="view_test_workflow_model" model="ir.ui.view">
|
||||
<field name="name">Test workflow</field>
|
||||
<field name="model">test.workflow.model</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Test workflow">
|
||||
<button name="a-b" string="a-b" type="workflow" icon="gtk-ok" colspan="1"/>
|
||||
<label string="a-b"/>
|
||||
<button name="trigger" string="trigger" type="object" icon="gtk-ok" colspan="1"/>
|
||||
<label string="trigger"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_test_workflow" model="ir.actions.act_window">
|
||||
<field name="name">Test workflow</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">test.workflow.model</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem icon="STOCK_PREFERENCES" id="base.menu_tests" name="Tests" sequence="1000000"/>
|
||||
|
||||
<menuitem id="menu_test_workflow" parent="base.menu_tests" name="Test workflow"/>
|
||||
|
||||
<menuitem id="menu_test_workflow_leaf"
|
||||
name="Test workflow"
|
||||
action="action_test_workflow"
|
||||
parent="menu_test_workflow"/>
|
||||
|
||||
|
||||
<record id="test_workflow_trigger_1" model="test.workflow.trigger">
|
||||
<!-- A single trigger record, with known ID 1 -->
|
||||
</record>
|
||||
|
||||
<!-- A simple workflow:
|
||||
|
||||
a -signal-> b -trigger-> c
|
||||
|
||||
-->
|
||||
<record id="test_workflow" model="workflow">
|
||||
<field name="name">test.workflow</field>
|
||||
<field name="osv">test.workflow.model</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">print_a()</field>
|
||||
</record>
|
||||
<record id="activity_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow"/>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">print_b()</field>
|
||||
</record>
|
||||
<record id="activity_c" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">c</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">print_c()</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_a"/>
|
||||
<field name="act_to" ref="activity_b"/>
|
||||
<field name="signal">a-b</field>
|
||||
</record>
|
||||
<record id="trans_b_c" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_b"/>
|
||||
<field name="act_to" ref="activity_c"/>
|
||||
<field name="condition">condition()</field>
|
||||
<field name="trigger_model">test.workflow.trigger</field>
|
||||
<field name="trigger_expr_id">[1]</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow A (a single activity):
|
||||
|
||||
a
|
||||
|
||||
-->
|
||||
<record id="test_workflow_a" model="workflow">
|
||||
<field name="name">test.workflow.a</field>
|
||||
<field name="osv">test.workflow.model.a</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_a_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_a"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">dummy</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow B (a single activity):
|
||||
|
||||
a
|
||||
|
||||
The function is run when the record is created.
|
||||
|
||||
-->
|
||||
<record id="test_workflow_b" model="workflow">
|
||||
<field name="name">test.workflow.b</field>
|
||||
<field name="osv">test.workflow.model.b</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_b_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_b"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow C (a single activity):
|
||||
|
||||
a
|
||||
|
||||
The function is not run when the kind is dummy and no action_id is provided.
|
||||
-->
|
||||
<record id="test_workflow_c" model="workflow">
|
||||
<field name="name">test.workflow.c</field>
|
||||
<field name="osv">test.workflow.model.c</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_c_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_c"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">dummy</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow D (a single activity):
|
||||
|
||||
a
|
||||
|
||||
The function is run when the kind is stopall and no action_id is provided.
|
||||
-->
|
||||
<record id="test_workflow_d" model="workflow">
|
||||
<field name="name">test.workflow.d</field>
|
||||
<field name="osv">test.workflow.model.d</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_d_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_d"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">stopall</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow E:
|
||||
|
||||
a -True-> b
|
||||
|
||||
Both activities are run when the workflow is instanciated.
|
||||
-->
|
||||
<record id="test_workflow_e" model="workflow">
|
||||
<field name="name">test.workflow.e</field>
|
||||
<field name="osv">test.workflow.model.e</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_e_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_e"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
</record>
|
||||
<record id="activity_e_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_e"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_e_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_e_a"/>
|
||||
<field name="act_to" ref="activity_e_b"/>
|
||||
</record>
|
||||
|
||||
<!-- Workflow F:
|
||||
|
||||
a -signal-> b
|
||||
|
||||
Same as E but with a signal on the transition.
|
||||
-->
|
||||
<record id="test_workflow_f" model="workflow">
|
||||
<field name="name">test.workflow.f</field>
|
||||
<field name="osv">test.workflow.model.f</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_f_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_f"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
</record>
|
||||
<record id="activity_f_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_f"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_f_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_f_a"/>
|
||||
<field name="act_to" ref="activity_f_b"/>
|
||||
<field name="signal">a-b</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow G:
|
||||
|
||||
a -False-> b
|
||||
|
||||
-->
|
||||
<record id="test_workflow_g" model="workflow">
|
||||
<field name="name">test.workflow.g</field>
|
||||
<field name="osv">test.workflow.model.g</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_g_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_g"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
</record>
|
||||
<record id="activity_g_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_g"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_g_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_g_a"/>
|
||||
<field name="act_to" ref="activity_g_b"/>
|
||||
<field name="condition">False</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow H:
|
||||
|
||||
a or -> b { value: 2 }
|
||||
`-> c { value: 2 }
|
||||
|
||||
Whether the action of b or c is exectued last is non-deterministic.
|
||||
|
||||
-->
|
||||
<record id="test_workflow_h" model="workflow">
|
||||
<field name="name">test.workflow.h</field>
|
||||
<field name="osv">test.workflow.model.h</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_h_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_h"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
<field name="split_mode">OR</field>
|
||||
</record>
|
||||
<record id="activity_h_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_h"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
<record id="activity_h_c" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_h"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">c</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_h_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_h_a"/>
|
||||
<field name="act_to" ref="activity_h_b"/>
|
||||
</record>
|
||||
<record id="trans_h_a_c" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_h_a"/>
|
||||
<field name="act_to" ref="activity_h_c"/>
|
||||
</record>
|
||||
|
||||
<!-- Workflow I:
|
||||
|
||||
a or -> b { value: 2 }
|
||||
`false> c { value: 3 }
|
||||
|
||||
-->
|
||||
<record id="test_workflow_i" model="workflow">
|
||||
<field name="name">test.workflow.i</field>
|
||||
<field name="osv">test.workflow.model.i</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_i_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_i"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
<field name="split_mode">OR</field>
|
||||
</record>
|
||||
<record id="activity_i_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_i"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
<record id="activity_i_c" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_i"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">c</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 3})</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_i_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_i_a"/>
|
||||
<field name="act_to" ref="activity_i_b"/>
|
||||
</record>
|
||||
<record id="trans_i_a_c" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_i_a"/>
|
||||
<field name="act_to" ref="activity_i_c"/>
|
||||
<field name="condition">False</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow J:
|
||||
|
||||
a and -> b { value: 2 }
|
||||
`False> c { value: 3 }
|
||||
|
||||
This will stay in a because all transitions should be True at the same time.
|
||||
|
||||
-->
|
||||
<record id="test_workflow_j" model="workflow">
|
||||
<field name="name">test.workflow.j</field>
|
||||
<field name="osv">test.workflow.model.j</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_j_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_j"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
<field name="split_mode">AND</field>
|
||||
</record>
|
||||
<record id="activity_j_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_j"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
<record id="activity_j_c" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_j"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">c</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 3})</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_j_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_j_a"/>
|
||||
<field name="act_to" ref="activity_j_b"/>
|
||||
</record>
|
||||
<record id="trans_j_a_c" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_j_a"/>
|
||||
<field name="act_to" ref="activity_j_c"/>
|
||||
<field name="condition">False</field>
|
||||
</record>
|
||||
|
||||
<!-- Workflow K:
|
||||
|
||||
a xor -> b { value: 2 }
|
||||
`> c { value: 2 }
|
||||
|
||||
Only one (truish) transition is taken with a XOR.
|
||||
|
||||
-->
|
||||
<record id="test_workflow_k" model="workflow">
|
||||
<field name="name">test.workflow.k</field>
|
||||
<field name="osv">test.workflow.model.k</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_k_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_k"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
<field name="split_mode">XOR</field>
|
||||
</record>
|
||||
<record id="activity_k_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_k"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
<record id="activity_k_c" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_k"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">c</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_k_a_b" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_k_a"/>
|
||||
<field name="act_to" ref="activity_k_b"/>
|
||||
</record>
|
||||
<record id="trans_k_a_c" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_k_a"/>
|
||||
<field name="act_to" ref="activity_k_c"/>
|
||||
</record>
|
||||
|
||||
<!-- Workflow L:
|
||||
|
||||
a -> xor c { value: 3 }
|
||||
b ´
|
||||
a -> and d { value: 3 }
|
||||
b ´
|
||||
|
||||
c is run for each incoming (and taken) transition.
|
||||
d is run once when all its incoming transitions are taken at the same time.
|
||||
|
||||
-->
|
||||
<record id="test_workflow_l" model="workflow">
|
||||
<field name="name">test.workflow.l</field>
|
||||
<field name="osv">test.workflow.model.l</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<record id="activity_l_a" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_l"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">a</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 1})</field>
|
||||
<field name="split_mode">OR</field>
|
||||
</record>
|
||||
<record id="activity_l_b" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_l"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">b</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 2})</field>
|
||||
<field name="split_mode">OR</field>
|
||||
</record>
|
||||
<record id="activity_l_c" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_l"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">c</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 3})</field>
|
||||
<field name="join_mode">XOR</field>
|
||||
</record>
|
||||
<record id="activity_l_d" model="workflow.activity">
|
||||
<field name="wkf_id" ref="test_workflow_l"/>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="name">d</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'value': 3})</field>
|
||||
<field name="join_mode">AND</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_l_a_c" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_l_a"/>
|
||||
<field name="act_to" ref="activity_l_c"/>
|
||||
</record>
|
||||
<record id="trans_l_b_c" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_l_b"/>
|
||||
<field name="act_to" ref="activity_l_c"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_l_a_d" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_l_a"/>
|
||||
<field name="act_to" ref="activity_l_d"/>
|
||||
</record>
|
||||
<record id="trans_l_b_d" model="workflow.transition">
|
||||
<field name="act_from" ref="activity_l_b"/>
|
||||
<field name="act_to" ref="activity_l_d"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,67 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import openerp
|
||||
|
||||
class m(openerp.osv.orm.Model):
|
||||
""" A model for which we will define a workflow (see data.xml). """
|
||||
_name = 'test.workflow.model'
|
||||
|
||||
def print_(self, cr, uid, ids, s, context=None):
|
||||
print ' Running activity `%s` for record %s' % (s, ids)
|
||||
return True
|
||||
|
||||
def print_a(self, cr, uid, ids, context=None):
|
||||
return self.print_(cr, uid, ids, 'a', context)
|
||||
|
||||
def print_b(self, cr, uid, ids, context=None):
|
||||
return self.print_(cr, uid, ids, 'b', context)
|
||||
|
||||
def print_c(self, cr, uid, ids, context=None):
|
||||
return self.print_(cr, uid, ids, 'c', context)
|
||||
|
||||
def condition(self, cr, uid, ids, context=None):
|
||||
m = self.pool['test.workflow.trigger']
|
||||
for r in m.browse(cr, uid, [1], context=context):
|
||||
if not r.value:
|
||||
return False
|
||||
return True
|
||||
|
||||
def trigger(self, cr, uid, context=None):
|
||||
return openerp.workflow.trg_trigger(uid, 'test.workflow.trigger', 1, cr)
|
||||
|
||||
class n(openerp.osv.orm.Model):
|
||||
""" A model used for the trigger feature. """
|
||||
_name = 'test.workflow.trigger'
|
||||
_columns = { 'value': openerp.osv.fields.boolean('Value') }
|
||||
_defaults = { 'value': False }
|
||||
|
||||
class a(openerp.osv.orm.Model):
|
||||
_name = 'test.workflow.model.a'
|
||||
_columns = { 'value': openerp.osv.fields.integer('Value') }
|
||||
_defaults = { 'value': 0 }
|
||||
|
||||
class b(openerp.osv.orm.Model):
|
||||
_name = 'test.workflow.model.b'
|
||||
_inherit = 'test.workflow.model.a'
|
||||
|
||||
class c(openerp.osv.orm.Model):
|
||||
_name = 'test.workflow.model.c'
|
||||
_inherit = 'test.workflow.model.a'
|
||||
|
||||
class d(openerp.osv.orm.Model):
|
||||
_name = 'test.workflow.model.d'
|
||||
_inherit = 'test.workflow.model.a'
|
||||
|
||||
class e(openerp.osv.orm.Model):
|
||||
_name = 'test.workflow.model.e'
|
||||
_inherit = 'test.workflow.model.a'
|
||||
|
||||
for name in 'bcdefghijkl':
|
||||
type(
|
||||
name,
|
||||
(openerp.osv.orm.Model,),
|
||||
{
|
||||
'_name': 'test.workflow.model.%s' % name,
|
||||
'_inherit': 'test.workflow.model.a',
|
||||
})
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import test_workflow
|
||||
|
||||
fast_suite = [
|
||||
]
|
||||
|
||||
checks = [
|
||||
test_workflow,
|
||||
]
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,183 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import openerp
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.tests import common
|
||||
|
||||
|
||||
class test_workflows(common.TransactionCase):
|
||||
|
||||
def check_activities(self, model_name, i, names):
|
||||
""" Check that the record i has workitems in the given activity names.
|
||||
"""
|
||||
instance = self.registry('workflow.instance')
|
||||
workitem = self.registry('workflow.workitem')
|
||||
|
||||
# Given the workflow instance associated to the record ...
|
||||
instance_id = instance.search(
|
||||
self.cr, SUPERUSER_ID,
|
||||
[('res_type', '=', model_name), ('res_id', '=', i)])
|
||||
self.assertTrue( instance_id, 'A workflow instance is expected.')
|
||||
|
||||
# ... get all its workitems ...
|
||||
workitem_ids = workitem.search(
|
||||
self.cr, SUPERUSER_ID,
|
||||
[('inst_id', '=', instance_id[0])])
|
||||
self.assertTrue(
|
||||
workitem_ids,
|
||||
'The workflow instance should have workitems.')
|
||||
|
||||
# ... and check the activity the are in against the provided names.
|
||||
workitem_records = workitem.browse(
|
||||
self.cr, SUPERUSER_ID, workitem_ids)
|
||||
self.assertEqual(
|
||||
sorted([item.act_id.name for item in workitem_records]),
|
||||
sorted(names))
|
||||
|
||||
def check_value(self, model_name, i, value):
|
||||
""" Check that the record i has the given value.
|
||||
"""
|
||||
model = self.registry(model_name)
|
||||
record = model.read(self.cr, SUPERUSER_ID, [i], ['value'])[0]
|
||||
self.assertEqual(record['value'], value)
|
||||
|
||||
def test_workflow(self):
|
||||
model = self.registry('test.workflow.model')
|
||||
trigger = self.registry('test.workflow.trigger')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
|
||||
# a -> b is just a signal.
|
||||
model.signal_workflow(self.cr, SUPERUSER_ID, [i], 'a-b')
|
||||
self.check_activities(model._name, i, ['b'])
|
||||
|
||||
# b -> c is a trigger (which is False),
|
||||
# so we remain in the b activity.
|
||||
model.trigger(self.cr, SUPERUSER_ID, [i])
|
||||
self.check_activities(model._name, i, ['b'])
|
||||
|
||||
# b -> c is a trigger (which is set to True).
|
||||
# so we go in c when the trigger is called.
|
||||
trigger.write(self.cr, SUPERUSER_ID, [1], {'value': True})
|
||||
model.trigger(self.cr, SUPERUSER_ID)
|
||||
self.check_activities(model._name, i, ['c'])
|
||||
|
||||
self.assertEqual(
|
||||
True,
|
||||
True)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_a(self):
|
||||
model = self.registry('test.workflow.model.a')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
self.check_value(model._name, i, 0)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_b(self):
|
||||
model = self.registry('test.workflow.model.b')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
self.check_value(model._name, i, 1)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_c(self):
|
||||
model = self.registry('test.workflow.model.c')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
self.check_value(model._name, i, 0)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_d(self):
|
||||
model = self.registry('test.workflow.model.d')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
self.check_value(model._name, i, 1)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_e(self):
|
||||
model = self.registry('test.workflow.model.e')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['b'])
|
||||
self.check_value(model._name, i, 2)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_f(self):
|
||||
model = self.registry('test.workflow.model.f')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
self.check_value(model._name, i, 1)
|
||||
|
||||
model.signal_workflow(self.cr, SUPERUSER_ID, [i], 'a-b')
|
||||
self.check_activities(model._name, i, ['b'])
|
||||
self.check_value(model._name, i, 2)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_g(self):
|
||||
model = self.registry('test.workflow.model.g')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
self.check_value(model._name, i, 1)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_h(self):
|
||||
model = self.registry('test.workflow.model.h')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['b', 'c'])
|
||||
self.check_value(model._name, i, 2)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_i(self):
|
||||
model = self.registry('test.workflow.model.i')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['b'])
|
||||
self.check_value(model._name, i, 2)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_j(self):
|
||||
model = self.registry('test.workflow.model.j')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['a'])
|
||||
self.check_value(model._name, i, 1)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_k(self):
|
||||
model = self.registry('test.workflow.model.k')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
# Non-determinisitic: can be b or c
|
||||
# self.check_activities(model._name, i, ['b'])
|
||||
# self.check_activities(model._name, i, ['c'])
|
||||
self.check_value(model._name, i, 2)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
||||
|
||||
def test_workflow_l(self):
|
||||
model = self.registry('test.workflow.model.l')
|
||||
|
||||
i = model.create(self.cr, SUPERUSER_ID, {})
|
||||
self.check_activities(model._name, i, ['c', 'c', 'd'])
|
||||
self.check_value(model._name, i, 3)
|
||||
|
||||
model.unlink(self.cr, SUPERUSER_ID, [i])
|
|
@ -272,18 +272,6 @@ def reverse_enumerate(l):
|
|||
"""
|
||||
return izip(xrange(len(l)-1, -1, -1), reversed(l))
|
||||
|
||||
#----------------------------------------------------------
|
||||
# SMS
|
||||
#----------------------------------------------------------
|
||||
# text must be latin-1 encoded
|
||||
def sms_send(user, password, api_id, text, to):
|
||||
import urllib
|
||||
url = "http://api.urlsms.com/SendSMS.aspx"
|
||||
#url = "http://196.7.150.220/http/sendmsg"
|
||||
params = urllib.urlencode({'UserID': user, 'Password': password, 'SenderID': api_id, 'MsgText': text, 'RecipientMobileNo':to})
|
||||
urllib.urlopen(url+"?"+params)
|
||||
# FIXME: Use the logger if there is an error
|
||||
return True
|
||||
|
||||
class UpdateableStr(local):
|
||||
""" Class that stores an updateable string (used in wizards)
|
||||
|
|
|
@ -19,62 +19,92 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Evaluate workflow code found in activity actions and transition conditions.
|
||||
"""
|
||||
|
||||
import openerp
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
|
||||
class Env(dict):
|
||||
def __init__(self, cr, uid, model, ids):
|
||||
"""
|
||||
Dictionary class used as an environment to evaluate workflow code (such as
|
||||
the condition on transitions).
|
||||
|
||||
This environment provides sybmols for cr, uid, id, model name, model
|
||||
instance, column names, and all the record (the one obtained by browsing
|
||||
the provided ID) attributes.
|
||||
"""
|
||||
def __init__(self, cr, uid, model, id):
|
||||
self.cr = cr
|
||||
self.uid = uid
|
||||
self.model = model
|
||||
self.ids = ids
|
||||
self.id = id
|
||||
self.ids = [id]
|
||||
self.obj = openerp.registry(cr.dbname)[model]
|
||||
self.columns = self.obj._columns.keys() + self.obj._inherit_fields.keys()
|
||||
|
||||
def __getitem__(self, key):
|
||||
if (key in self.columns) or (key in dir(self.obj)):
|
||||
res = self.obj.browse(self.cr, self.uid, self.ids[0])
|
||||
res = self.obj.browse(self.cr, self.uid, self.id)
|
||||
return res[key]
|
||||
else:
|
||||
return super(Env, self).__getitem__(key)
|
||||
|
||||
def _eval_expr(cr, ident, workitem, action):
|
||||
ret=False
|
||||
assert action, 'You used a NULL action in a workflow, use dummy node instead.'
|
||||
for line in action.split('\n'):
|
||||
def _eval_expr(cr, ident, workitem, lines):
|
||||
"""
|
||||
Evaluate each line of ``lines`` with the ``Env`` environment, returning
|
||||
the value of the last line.
|
||||
"""
|
||||
assert lines, 'You used a NULL action in a workflow, use dummy node instead.'
|
||||
uid, model, id = ident
|
||||
result = False
|
||||
for line in lines.split('\n'):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
uid=ident[0]
|
||||
model=ident[1]
|
||||
ids=[ident[2]]
|
||||
if line =='True':
|
||||
ret=True
|
||||
elif line =='False':
|
||||
ret=False
|
||||
if line == 'True':
|
||||
result = True
|
||||
elif line == 'False':
|
||||
result = False
|
||||
else:
|
||||
env = Env(cr, uid, model, ids)
|
||||
ret = eval(line, env, nocopy=True)
|
||||
return ret
|
||||
env = Env(cr, uid, model, id)
|
||||
result = eval(line, env, nocopy=True)
|
||||
return result
|
||||
|
||||
def execute_action(cr, ident, workitem, activity):
|
||||
obj = openerp.registry(cr.dbname)['ir.actions.server']
|
||||
ctx = {'active_model':ident[1], 'active_id':ident[2], 'active_ids':[ident[2]]}
|
||||
result = obj.run(cr, ident[0], [activity['action_id']], ctx)
|
||||
"""
|
||||
Evaluate the ir.actions.server action specified in the activity.
|
||||
"""
|
||||
uid, model, id = ident
|
||||
ir_actions_server = openerp.registry(cr.dbname)['ir.actions.server']
|
||||
context = { 'active_model': model, 'active_id': id, 'active_ids': [id] }
|
||||
result = ir_actions_server.run(cr, uid, [activity['action_id']], context)
|
||||
return result
|
||||
|
||||
def execute(cr, ident, workitem, activity):
|
||||
"""
|
||||
Evaluate the action specified in the activity.
|
||||
"""
|
||||
return _eval_expr(cr, ident, workitem, activity['action'])
|
||||
|
||||
def check(cr, workitem, ident, transition, signal):
|
||||
"""
|
||||
Test if a transition can be taken. The transition can be taken if:
|
||||
|
||||
- the signal name matches,
|
||||
- the uid is SUPERUSER_ID or the user groups contains the transition's
|
||||
group,
|
||||
- the condition evaluates to a truish value.
|
||||
"""
|
||||
if transition['signal'] and signal != transition['signal']:
|
||||
return False
|
||||
|
||||
uid = ident[0]
|
||||
if transition['group_id'] and uid != 1:
|
||||
if uid != openerp.SUPERUSER_ID and transition['groups_id']:
|
||||
registry = openerp.registry(cr.dbname)
|
||||
user_groups = registry['res.users'].read(cr, uid, [uid], ['groups_id'])[0]['groups_id']
|
||||
if not transition['group_id'] in user_groups:
|
||||
if transition['group_id'] not in user_groups:
|
||||
return False
|
||||
|
||||
return _eval_expr(cr, ident, workitem, transition['condition'])
|
||||
|
|
|
@ -19,9 +19,10 @@ from . import scaffold
|
|||
from . import uninstall
|
||||
from . import update
|
||||
from . import web
|
||||
from . import grunt_tests
|
||||
|
||||
command_list_server = (conf, cron, drop, initialize, model, module, read, run_tests,
|
||||
scaffold, uninstall, update, web, )
|
||||
scaffold, uninstall, update, web, grunt_tests, )
|
||||
|
||||
command_list_client = (Call, Open, Show, ConsumeNothing, ConsumeMemory,
|
||||
LeakMemory, ConsumeCPU, Bench, BenchRead,
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
"""
|
||||
Search for Gruntfile.js files in all the addons and launch them using the 'grunt test' command.
|
||||
"""
|
||||
|
||||
import common
|
||||
import fnmatch
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
def grunt_tester(directories, log = sys.stdout):
|
||||
result = 0
|
||||
|
||||
matches = []
|
||||
for direc in directories:
|
||||
for root, dirnames, filenames in os.walk(direc):
|
||||
for filename in fnmatch.filter(filenames, 'Gruntfile.js'):
|
||||
full = os.path.join(root, filename)
|
||||
if re.match(r"(^.*?/node_modules/.*$)|(^.*?/lib/.*$)", full):
|
||||
continue
|
||||
matches.append(full)
|
||||
|
||||
for file_ in matches:
|
||||
folder = os.path.dirname(file_)
|
||||
p = subprocess.Popen(['npm', 'install'], cwd=folder)
|
||||
if p.wait() != 0:
|
||||
raise Exception("Failed to install dependencies for Gruntfile located in folder %s" % folder)
|
||||
|
||||
p = subprocess.Popen(['grunt', 'test', '--no-color'], cwd=folder, stdout=log, stderr=log)
|
||||
if p.wait() != 0:
|
||||
result = 1
|
||||
|
||||
return result
|
||||
|
||||
def run(args):
|
||||
if args.addons:
|
||||
args.addons = args.addons.split(':')
|
||||
else:
|
||||
args.addons = []
|
||||
result = grunt_tester(args.addons)
|
||||
if result != 0:
|
||||
sys.exit(result)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('grunt-tests',
|
||||
description='Run the tests contained in Gruntfile.js files.')
|
||||
common.add_addons_argument(parser)
|
||||
|
||||
parser.set_defaults(run=run)
|
Loading…
Reference in New Issue