diff options
| author | Xavier Morel <xmo@openerp.com> | 2014-11-03 17:31:54 +0100 |
|---|---|---|
| committer | Xavier Morel <xmo@openerp.com> | 2014-11-03 17:39:24 +0100 |
| commit | 03e9a38a1d1586139d61b753ecfcf7373e6b1f2c (patch) | |
| tree | 6aad04672595efcd5e2fae66e169ca9d5f37ff5e /doc/reference | |
| parent | fb6892f8577087c1902c2a85dfc8badeac98aafa (diff) | |
[IMP] doc: remove guides section, move themes to tutorials and rest to reference
also pass PNGs through crusher, turns out dot produces pretty terrible PNGs
Diffstat (limited to 'doc/reference')
36 files changed, 889 insertions, 5 deletions
diff --git a/doc/reference/forms/header.png b/doc/reference/forms/header.png Binary files differnew file mode 100644 index 00000000000..bf97b8cd05c --- /dev/null +++ b/doc/reference/forms/header.png diff --git a/doc/reference/forms/header2.png b/doc/reference/forms/header2.png Binary files differnew file mode 100644 index 00000000000..0d0dbbf9eca --- /dev/null +++ b/doc/reference/forms/header2.png diff --git a/doc/reference/forms/header3.png b/doc/reference/forms/header3.png Binary files differnew file mode 100644 index 00000000000..17e8fb9c2fd --- /dev/null +++ b/doc/reference/forms/header3.png diff --git a/doc/reference/forms/nosheet.png b/doc/reference/forms/nosheet.png Binary files differnew file mode 100644 index 00000000000..99d915c98f0 --- /dev/null +++ b/doc/reference/forms/nosheet.png diff --git a/doc/reference/forms/oppreadonly.png b/doc/reference/forms/oppreadonly.png Binary files differnew file mode 100644 index 00000000000..447e7bbffa5 --- /dev/null +++ b/doc/reference/forms/oppreadonly.png diff --git a/doc/reference/forms/placeholder.png b/doc/reference/forms/placeholder.png Binary files differnew file mode 100644 index 00000000000..ab7d592f885 --- /dev/null +++ b/doc/reference/forms/placeholder.png diff --git a/doc/reference/forms/screenshot-00.png b/doc/reference/forms/screenshot-00.png Binary files differnew file mode 100644 index 00000000000..dca05afeb2a --- /dev/null +++ b/doc/reference/forms/screenshot-00.png diff --git a/doc/reference/forms/screenshot-01.png b/doc/reference/forms/screenshot-01.png Binary files differnew file mode 100644 index 00000000000..464a23740a1 --- /dev/null +++ b/doc/reference/forms/screenshot-01.png diff --git a/doc/reference/forms/screenshot-02.png b/doc/reference/forms/screenshot-02.png Binary files differnew file mode 100644 index 00000000000..ea6ec7705e2 --- /dev/null +++ b/doc/reference/forms/screenshot-02.png diff --git a/doc/reference/forms/screenshot-03.png b/doc/reference/forms/screenshot-03.png Binary files differnew file mode 100644 index 00000000000..3a59801cff9 --- /dev/null +++ b/doc/reference/forms/screenshot-03.png diff --git a/doc/reference/forms/screenshot-04.png b/doc/reference/forms/screenshot-04.png Binary files differnew file mode 100644 index 00000000000..b0de67d5185 --- /dev/null +++ b/doc/reference/forms/screenshot-04.png diff --git a/doc/reference/forms/sheet.png b/doc/reference/forms/sheet.png Binary files differnew file mode 100644 index 00000000000..dee87a0e4b7 --- /dev/null +++ b/doc/reference/forms/sheet.png diff --git a/doc/reference/forms/status.png b/doc/reference/forms/status.png Binary files differnew file mode 100644 index 00000000000..4f4a0cb1c11 --- /dev/null +++ b/doc/reference/forms/status.png diff --git a/doc/reference/forms/status1.png b/doc/reference/forms/status1.png Binary files differnew file mode 100644 index 00000000000..7ccd80bb532 --- /dev/null +++ b/doc/reference/forms/status1.png diff --git a/doc/reference/forms/status2.png b/doc/reference/forms/status2.png Binary files differnew file mode 100644 index 00000000000..b0d01bd2e97 --- /dev/null +++ b/doc/reference/forms/status2.png diff --git a/doc/reference/forms/wizard-popup.png b/doc/reference/forms/wizard-popup.png Binary files differnew file mode 100644 index 00000000000..d36549f8687 --- /dev/null +++ b/doc/reference/forms/wizard-popup.png diff --git a/doc/reference/images/runner.png b/doc/reference/images/runner.png Binary files differindex bd48e9d2922..9991fce45a1 100644 --- a/doc/reference/images/runner.png +++ b/doc/reference/images/runner.png diff --git a/doc/reference/images/runner2.png b/doc/reference/images/runner2.png Binary files differindex 38ea2949cfc..ff8f5bfe722 100644 --- a/doc/reference/images/runner2.png +++ b/doc/reference/images/runner2.png diff --git a/doc/reference/images/tests.png b/doc/reference/images/tests.png Binary files differindex 84083d9e5ed..bc3ce8b43fc 100644 --- a/doc/reference/images/tests.png +++ b/doc/reference/images/tests.png diff --git a/doc/reference/images/tests2.png b/doc/reference/images/tests2.png Binary files differindex c8a6f8ae9ee..361fb7fb54b 100644 --- a/doc/reference/images/tests2.png +++ b/doc/reference/images/tests2.png diff --git a/doc/reference/images/tests3.png b/doc/reference/images/tests3.png Binary files differindex 247f70716af..d323360497d 100644 --- a/doc/reference/images/tests3.png +++ b/doc/reference/images/tests3.png diff --git a/doc/reference/views.rst b/doc/reference/views.rst index 3b678a37b3c..9eaab61b324 100644 --- a/doc/reference/views.rst +++ b/doc/reference/views.rst @@ -1,3 +1,5 @@ +.. highlight:: xml + .. _reference/views: ===== @@ -6,8 +8,8 @@ Views .. _reference/views/structure: -Structure -========= +Common Structure +================ View objects expose a number of fields, they are optional unless specified otherwise) @@ -421,6 +423,315 @@ system. Available semantic components are: .. todo:: widgets? +Business Views guidelines +------------------------- + +.. sectionauthor:: Aline Preillon, Raphael Collet + +Business views are targeted at regular users, not advanced users. Examples +are: Opportunities, Products, Partners, Tasks, Projects, etc. + +.. image:: forms/oppreadonly.png + :class: img-responsive + +In general, a business view is composed of + +1. a status bar on top (with technical or business flow), +2. a sheet in the middle (the form itself), +3. a bottom part with History and Comments. + +Technically, the new form views are structured as follows in XML:: + + <form> + <header> ... content of the status bar ... </header> + <sheet> ... content of the sheet ... </sheet> + <div class="oe_chatter"> ... content of the bottom part ... </div> + </form> + +The Status Bar +'''''''''''''' + +The purpose of the status bar is to show the status of the current record and +the action buttons. + +.. image:: forms/status.png + :class: img-responsive + +The Buttons +........... + +The order of buttons follows the business flow. For instance, in a sale order, +the logical steps are: + +1. Send the quotation +2. Confirm the quotation +3. Create the final invoice +4. Send the goods + +Highlighted buttons (in red by default) emphasize the logical next step, to +help the user. It is usually the first active button. On the other hand, +:guilabel:`cancel` buttons *must* remain grey (normal). For instance, in +Invoice the button :guilabel:`Refund` must never be red. + +Technically, buttons are highlighted by adding the class "oe_highlight":: + + <button class="oe_highlight" name="..." type="..." states="..."/> + +The Status +.......... + +Uses the ``statusbar`` widget, and shows the current state in red. States +common to all flows (for instance, a sale order begins as a quotation, then we +send it, then it becomes a full sale order, and finally it is done) should be +visible at all times but exceptions or states depending on particular sub-flow +should only be visible when current. + +.. image:: forms/status1.png + :class: img-responsive + +.. image:: forms/status2.png + :class: img-responsive + +The states are shown following the order used in the field (the list in a +selection field, etc). States that are always visible are specified with the +attribute ``statusbar_visible``. + +``statusbar_colors`` can be used to give a custom color to specific states. + +:: + + <field name="state" widget="statusbar" + statusbar_visible="draft,sent,progress,invoiced,done" + statusbar_colors="{'shipping_except':'red','waiting_date':'blue'}"/> + +The Sheet +''''''''' + +All business views should look like a printed sheet: + +.. image:: forms/sheet.png + :class: img-responsive + +1. Elements inside a ``<form>`` or ``<page>`` do not define groups, elements + inside them are laid out according to normal HTML rules. They content can + be explicitly grouped using ``<group>`` or regular ``<div>`` elements. +2. By default, the element ``<group>`` defines two columns inside, unless an + attribute ``col="n"`` is used. The columns have the same width (1/n th of + the group's width). Use a ``<group>`` element to produce a column of fields. +3. To give a title to a section, add a ``string`` attribute to a ``<group>`` element:: + + <group string="Time-sensitive operations"> + + this replaces the former use of ``<separator string="XXX"/>``. +4. The ``<field>`` element does not produce a label, except as direct children + of a ``<group>`` element\ [#backwards-compatibility]_. Use :samp:`<label + for="{field_name}>` to produce a label of a field. + +Sheet Headers +............. + +Some sheets have headers with one or more fields, and the labels of those +fields are only shown in edit mode. + +.. list-table:: + :header-rows: 1 + + * - View mode + - Edit mode + * - .. image:: forms/header.png + :class: img-responsive + - .. image:: forms/header2.png + :class: img-responsive + +Use HTML text, ``<div>``, ``<h1>``, ``<h2>``… to produce nice headers, and +``<label>`` with the class ``oe_edit_only`` to only display the field's label +in edit mode. The class ``oe_inline`` will make fields inline (instead of +blocks): content following the field will be displayed on the same line rather +than on the line below it. The form above is produced by the following XML:: + + <label for="name" class="oe_edit_only"/> + <h1><field name="name"/></h1> + + <label for="planned_revenue" class="oe_edit_only"/> + <h2> + <field name="planned_revenue" class="oe_inline"/> + <field name="company_currency" class="oe_inline oe_edit_only"/> at + <field name="probability" class="oe_inline"/> % success rate + </h2> + +Button Box +.......... + +Many relevant actions or links can be displayed in the form. For example, in +Opportunity form, the actions "Schedule a Call" and "Schedule a Meeting" take +an important place in the use of the CRM. Instead of placing them in the +"More" menu, put them directly in the sheet as buttons (on the top right) to +make them more visible and more easily accessible. + +.. image:: forms/header3.png + :class: img-responsive + +Technically, the buttons are placed inside a <div> to group them as a block on +the right-hand side of the sheet. + +:: + + <div class="oe_button_box oe_right"> + <button string="Schedule/Log Call" name="..." type="action"/> + <button string="Schedule Meeting" name="action_makeMeeting" type="object"/> + </div> + +Groups and Titles +................. + +A column of fields is now produced with a ``<group>`` element, with an +optional title. + +.. image:: forms/screenshot-03.png + :class: img-responsive + +:: + + <group string="Payment Options"> + <field name="writeoff_amount"/> + <field name="payment_option"/> + </group> + +It is recommended to have two columns of fields on the form. For this, simply +put the ``<group>`` elements that contain the fields inside a top-level +``<group>`` element. + +To make :ref:`view extension <reference/views/inheritance>` simpler, it is +recommended to put a ``name`` attribute on ``<group>`` elements, so new fields +can easily be added at the right place. + +Special Case: Subtotals +~~~~~~~~~~~~~~~~~~~~~~~ + +Some classes are defined to render subtotals like in invoice forms: + +.. image:: forms/screenshot-00.png + :class: img-responsive + +:: + + <group class="oe_subtotal_footer"> + <field name="amount_untaxed"/> + <field name="amount_tax"/> + <field name="amount_total" class="oe_subtotal_footer_separator"/> + <field name="residual" style="margin-top: 10px"/> + </group> + +Placeholders and Inline Fields +.............................. + +Sometimes field labels make the form too complex. One can omit field labels, +and instead put a placeholder inside the field. The placeholder text is +visible only when the field is empty. The placeholder should tell what to +place inside the field, it *must not* be an example as they are often confused +with filled data. + +One can also group fields together by rendering them "inline" inside an +explicit block element like `<div>``. This allows grouping semantically +related fields as if they were a single (composite) fields. + +The following example, taken from the *Leads* form, shows both placeholders and +inline fields (zip and city). + +.. list-table:: + :header-rows: 1 + + * - Edit mode + - View mode + * - .. image:: forms/placeholder.png + :class: img-responsive + - .. image:: forms/screenshot-01.png + :class: img-responsive + +:: + + <group> + <label for="street" string="Address"/> + <div> + <field name="street" placeholder="Street..."/> + <field name="street2"/> + <div> + <field name="zip" class="oe_inline" placeholder="ZIP"/> + <field name="city" class="oe_inline" placeholder="City"/> + </div> + <field name="state_id" placeholder="State"/> + <field name="country_id" placeholder="Country"/> + </div> + </group> + +Images +...... + +Images, like avatars, should be displayed on the right of the sheet. The +product form looks like: + +.. image:: forms/screenshot-02.png + :class: img-responsive + +The form above contains a <sheet> element that starts with: + +:: + + <field name="product_image" widget="image" class="oe_avatar oe_right"/> + +Tags +.... + +Most :class:`~openerp.fields.Many2many` fields, like categories, are better +rendered as a list of tags. Use the widget ``many2many_tags`` for this: + +.. image:: forms/screenshot-04.png + :class: img-responsive + +:: + + <field name="category_id" widget="many2many_tags"/> + +Configuration forms guidelines +------------------------------ + +Examples of configuration forms: Stages, Leave Type, etc. This concerns all +menu items under Configuration of each application (like Sales/Configuration). + +.. image:: forms/nosheet.png + :class: img-responsive + +1. no header (because no state, no workflow, no button) +2. no sheet + +Dialog forms guidelines +----------------------- + +Example: "Schedule a Call" from an opportunity. + +.. image:: forms/wizard-popup.png + :class: img-responsive + +1. avoid separators (the title is already in the popup title bar, so another + separator is not relevant) +2. avoid cancel buttons (user generally close the popup window to get the same + effect) +3. action buttons must be highlighted (red) +4. when there is a text area, use a placeholder instead of a label or a + separator +5. like in regular form views, put buttons in the <header> element + +Configuration Wizards guidelines +-------------------------------- + +Example: Settings / Configuration / Sales. + +1. always in line (no popup) +2. no sheet +3. keep the cancel button (users cannot close the window) +4. the button "Apply" must be red + + .. _reference/views/graph: Graphs @@ -846,7 +1157,7 @@ Possible children elements of the search view are: as inclusively composited: they will be composed with ``OR`` rather than the usual ``AND``, e.g. - .. code-block:: xml + :: <filter domain="[('state', '=', 'draft')]"/> <filter domain="[('state', '=', 'done')]"/> @@ -854,7 +1165,7 @@ Possible children elements of the search view are: if both filters are selected, will select the records whose ``state`` is ``draft`` or ``done``, but - .. code-block:: xml + :: <filter domain="[('state', '=', 'draft')]"/> <separator/> @@ -877,7 +1188,9 @@ Search defaults Search fields and filters can be configured through the action's ``context`` using :samp:`search_default_{name}` keys. For fields, the value should be the value to set in the field, for filters it's a boolean value. For instance, -assuming ``foo`` is a field and ``bar`` is a filter an action context of:: +assuming ``foo`` is a field and ``bar`` is a filter an action context of: + +.. code-block:: python { 'search_default_foo': 'acro', @@ -902,6 +1215,7 @@ template's name *must* match the view's complete (including module name) :ref:`reference/data/template` should be used as a shortcut to define QWeb views. +.. [#backwards-compatibility] for backwards compatibility reasons .. [#hasclass] an extension function is added for simpler matching in QWeb views: ``hasclass(*classes)`` matches if the context node has all the specified classes diff --git a/doc/reference/workflow/Makefile b/doc/reference/workflow/Makefile new file mode 100644 index 00000000000..07bbb67dd55 --- /dev/null +++ b/doc/reference/workflow/Makefile @@ -0,0 +1,20 @@ +DOTFILES:=$(wildcard *.dot) +SVGFILES:=$(patsubst %.dot,%.svg,$(DOTFILES)) +PNGFILES:=$(patsubst %.dot,%.png,$(DOTFILES)) + +# try to disable implicit rules +.SUFFIXES: + +.PHONY: all clean + +all: $(SVGFILES) $(PNGFILES) + +# must -f to ignore errors when running clean multiple times in a row +clean: + rm -f *.png *.svg + +%.svg: %.dot + dot -Tsvg $< > $@ + +%.png: %.dot + dot -Tpng $< > $@ diff --git a/doc/reference/workflow/join.dot b/doc/reference/workflow/join.dot new file mode 100644 index 00000000000..e413a8a8ebc --- /dev/null +++ b/doc/reference/workflow/join.dot @@ -0,0 +1,10 @@ +digraph join { + // dummy sources as support for edges, make invisible and height 0 + a [style=invis height=0 fontsize=0] + b [style=invis height=0 fontsize=0] + c [style=invis height=0 fontsize=0] + + a -> Activity + b -> Activity + c -> Activity +} diff --git a/doc/reference/workflow/join.png b/doc/reference/workflow/join.png Binary files differnew file mode 100644 index 00000000000..cdc0bafb2ca --- /dev/null +++ b/doc/reference/workflow/join.png diff --git a/doc/reference/workflow/join.svg b/doc/reference/workflow/join.svg new file mode 100644 index 00000000000..1462281977f --- /dev/null +++ b/doc/reference/workflow/join.svg @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.38.0 (20140413.2041) + --> +<!-- Title: join Pages: 1 --> +<svg width="206pt" height="93pt" + viewBox="0.00 0.00 206.00 92.73" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 88.7279)"> +<title>join</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-88.7279 202,-88.7279 202,4 -4,4"/> +<!-- a --> +<!-- Activity --> +<g id="node4" class="node"><title>Activity</title> +<ellipse fill="none" stroke="black" cx="99" cy="-18" rx="40.0939" ry="18"/> +<text text-anchor="middle" x="99" y="-14.3" font-family="Times,serif" font-size="14.00">Activity</text> +</g> +<!-- a->Activity --> +<g id="edge1" class="edge"><title>a->Activity</title> +<path fill="none" stroke="black" d="M33.6445,-71.9778C42.4332,-64.8537 58.4442,-51.875 72.4186,-40.5472"/> +<polygon fill="black" stroke="black" points="74.7768,-43.1411 80.3411,-34.1251 70.3688,-37.7033 74.7768,-43.1411"/> +</g> +<!-- b --> +<!-- b->Activity --> +<g id="edge2" class="edge"><title>b->Activity</title> +<path fill="none" stroke="black" d="M99,-71.9778C99,-66.0508 99,-56.0715 99,-46.3619"/> +<polygon fill="black" stroke="black" points="102.5,-46.1364 99,-36.1364 95.5001,-46.1365 102.5,-46.1364"/> +</g> +<!-- c --> +<!-- c->Activity --> +<g id="edge3" class="edge"><title>c->Activity</title> +<path fill="none" stroke="black" d="M164.355,-71.9778C155.567,-64.8537 139.556,-51.875 125.581,-40.5472"/> +<polygon fill="black" stroke="black" points="127.631,-37.7033 117.659,-34.1251 123.223,-43.1411 127.631,-37.7033"/> +</g> +</g> +</svg> diff --git a/doc/reference/workflow/order_0.dot b/doc/reference/workflow/order_0.dot new file mode 100644 index 00000000000..3932040e914 --- /dev/null +++ b/doc/reference/workflow/order_0.dot @@ -0,0 +1,9 @@ +digraph order { + Draft [style=filled fillcolor="#73fa79"] + Closed [style=filled fillcolor="#98c7df"] + Canceled [style=filled fillcolor="#98c7df"] + + Draft -> Confirmed + Confirmed -> Closed + Confirmed -> Canceled +} diff --git a/doc/reference/workflow/order_0.png b/doc/reference/workflow/order_0.png Binary files differnew file mode 100644 index 00000000000..7d16f96dcbc --- /dev/null +++ b/doc/reference/workflow/order_0.png diff --git a/doc/reference/workflow/order_0.svg b/doc/reference/workflow/order_0.svg new file mode 100644 index 00000000000..54a5c050238 --- /dev/null +++ b/doc/reference/workflow/order_0.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.38.0 (20140413.2041) + --> +<!-- Title: order Pages: 1 --> +<svg width="188pt" height="188pt" + viewBox="0.00 0.00 188.24 188.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 184)"> +<title>order</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-184 184.243,-184 184.243,4 -4,4"/> +<!-- Draft --> +<g id="node1" class="node"><title>Draft</title> +<ellipse fill="#73fa79" stroke="black" cx="85.3968" cy="-162" rx="30.5947" ry="18"/> +<text text-anchor="middle" x="85.3968" y="-158.3" font-family="Times,serif" font-size="14.00">Draft</text> +</g> +<!-- Confirmed --> +<g id="node4" class="node"><title>Confirmed</title> +<ellipse fill="none" stroke="black" cx="85.3968" cy="-90" rx="51.9908" ry="18"/> +<text text-anchor="middle" x="85.3968" y="-86.3" font-family="Times,serif" font-size="14.00">Confirmed</text> +</g> +<!-- Draft->Confirmed --> +<g id="edge1" class="edge"><title>Draft->Confirmed</title> +<path fill="none" stroke="black" d="M85.3968,-143.697C85.3968,-135.983 85.3968,-126.712 85.3968,-118.112"/> +<polygon fill="black" stroke="black" points="88.8969,-118.104 85.3968,-108.104 81.8969,-118.104 88.8969,-118.104"/> +</g> +<!-- Closed --> +<g id="node2" class="node"><title>Closed</title> +<ellipse fill="#98c7df" stroke="black" cx="36.3968" cy="-18" rx="36.2938" ry="18"/> +<text text-anchor="middle" x="36.3968" y="-14.3" font-family="Times,serif" font-size="14.00">Closed</text> +</g> +<!-- Canceled --> +<g id="node3" class="node"><title>Canceled</title> +<ellipse fill="#98c7df" stroke="black" cx="135.397" cy="-18" rx="44.6926" ry="18"/> +<text text-anchor="middle" x="135.397" y="-14.3" font-family="Times,serif" font-size="14.00">Canceled</text> +</g> +<!-- Confirmed->Closed --> +<g id="edge2" class="edge"><title>Confirmed->Closed</title> +<path fill="none" stroke="black" d="M73.7845,-72.411C67.8042,-63.8677 60.3917,-53.2785 53.7479,-43.7874"/> +<polygon fill="black" stroke="black" points="56.5277,-41.6552 47.9257,-35.4699 50.7931,-45.6694 56.5277,-41.6552"/> +</g> +<!-- Confirmed->Canceled --> +<g id="edge3" class="edge"><title>Confirmed->Canceled</title> +<path fill="none" stroke="black" d="M97.2461,-72.411C103.348,-63.8677 110.912,-53.2785 117.692,-43.7874"/> +<polygon fill="black" stroke="black" points="120.668,-45.6416 123.633,-35.4699 114.972,-41.573 120.668,-45.6416"/> +</g> +</g> +</svg> diff --git a/doc/reference/workflow/order_1.dot b/doc/reference/workflow/order_1.dot new file mode 100644 index 00000000000..cc204343172 --- /dev/null +++ b/doc/reference/workflow/order_1.dot @@ -0,0 +1,11 @@ +digraph order { + Draft [style=filled fillcolor="#73fa79"] + Closed [style=filled fillcolor="#98c7df"] + Canceled [style=filled fillcolor="#98c7df"] + + Draft -> Confirmed [label="discount <= 15%"] + Draft -> Validation [label="discount > 15%"] + Validation -> Confirmed [label="Accept"] + Confirmed -> Closed + Confirmed -> Canceled +} diff --git a/doc/reference/workflow/order_1.png b/doc/reference/workflow/order_1.png Binary files differnew file mode 100644 index 00000000000..0c38acd0413 --- /dev/null +++ b/doc/reference/workflow/order_1.png diff --git a/doc/reference/workflow/order_1.svg b/doc/reference/workflow/order_1.svg new file mode 100644 index 00000000000..08f099e34c9 --- /dev/null +++ b/doc/reference/workflow/order_1.svg @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.38.0 (20140413.2041) + --> +<!-- Title: order Pages: 1 --> +<svg width="260pt" height="291pt" + viewBox="0.00 0.00 259.79 291.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 287)"> +<title>order</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-287 255.792,-287 255.792,4 -4,4"/> +<!-- Draft --> +<g id="node1" class="node"><title>Draft</title> +<ellipse fill="#73fa79" stroke="black" cx="85.3968" cy="-265" rx="30.5947" ry="18"/> +<text text-anchor="middle" x="85.3968" y="-261.3" font-family="Times,serif" font-size="14.00">Draft</text> +</g> +<!-- Confirmed --> +<g id="node4" class="node"><title>Confirmed</title> +<ellipse fill="none" stroke="black" cx="85.3968" cy="-91" rx="51.9908" ry="18"/> +<text text-anchor="middle" x="85.3968" y="-87.3" font-family="Times,serif" font-size="14.00">Confirmed</text> +</g> +<!-- Draft->Confirmed --> +<g id="edge1" class="edge"><title>Draft->Confirmed</title> +<path fill="none" stroke="black" d="M72.5816,-248.399C62.7132,-235.312 49.8641,-215.656 44.3968,-196 40.1092,-180.585 40.1092,-175.415 44.3968,-160 48.7071,-144.503 57.6054,-129.007 66.0142,-116.73"/> +<polygon fill="black" stroke="black" points="68.9995,-118.572 71.9779,-108.405 63.3089,-114.496 68.9995,-118.572"/> +<text text-anchor="middle" x="94.3968" y="-174.3" font-family="Times,serif" font-size="14.00">discount <= 15%</text> +</g> +<!-- Validation --> +<g id="node5" class="node"><title>Validation</title> +<ellipse fill="none" stroke="black" cx="202.397" cy="-178" rx="49.2915" ry="18"/> +<text text-anchor="middle" x="202.397" y="-174.3" font-family="Times,serif" font-size="14.00">Validation</text> +</g> +<!-- Draft->Validation --> +<g id="edge2" class="edge"><title>Draft->Validation</title> +<path fill="none" stroke="black" d="M103.936,-250.531C122.48,-237.059 151.286,-216.131 172.959,-200.386"/> +<polygon fill="black" stroke="black" points="175.139,-203.129 181.172,-194.42 171.024,-197.466 175.139,-203.129"/> +<text text-anchor="middle" x="195.897" y="-217.8" font-family="Times,serif" font-size="14.00">discount > 15%</text> +</g> +<!-- Closed --> +<g id="node2" class="node"><title>Closed</title> +<ellipse fill="#98c7df" stroke="black" cx="36.3968" cy="-18" rx="36.2938" ry="18"/> +<text text-anchor="middle" x="36.3968" y="-14.3" font-family="Times,serif" font-size="14.00">Closed</text> +</g> +<!-- Canceled --> +<g id="node3" class="node"><title>Canceled</title> +<ellipse fill="#98c7df" stroke="black" cx="135.397" cy="-18" rx="44.6926" ry="18"/> +<text text-anchor="middle" x="135.397" y="-14.3" font-family="Times,serif" font-size="14.00">Canceled</text> +</g> +<!-- Confirmed->Closed --> +<g id="edge4" class="edge"><title>Confirmed->Closed</title> +<path fill="none" stroke="black" d="M73.7845,-73.174C67.7175,-64.3831 60.1766,-53.4564 53.4596,-43.7236"/> +<polygon fill="black" stroke="black" points="56.1491,-41.4588 47.5884,-35.2165 50.3879,-45.4348 56.1491,-41.4588"/> +</g> +<!-- Confirmed->Canceled --> +<g id="edge5" class="edge"><title>Confirmed->Canceled</title> +<path fill="none" stroke="black" d="M97.2461,-73.174C103.348,-64.5087 110.912,-53.7682 117.692,-44.1415"/> +<polygon fill="black" stroke="black" points="120.736,-45.8965 123.633,-35.7052 115.013,-41.8661 120.736,-45.8965"/> +</g> +<!-- Validation->Confirmed --> +<g id="edge3" class="edge"><title>Validation->Confirmed</title> +<path fill="none" stroke="black" d="M181.209,-161.607C162.688,-148.152 135.604,-128.475 114.953,-113.472"/> +<polygon fill="black" stroke="black" points="116.97,-110.612 106.822,-107.566 112.856,-116.275 116.97,-110.612"/> +<text text-anchor="middle" x="170.397" y="-130.8" font-family="Times,serif" font-size="14.00">Accept</text> +</g> +</g> +</svg> diff --git a/doc/reference/workflow/split.dot b/doc/reference/workflow/split.dot new file mode 100644 index 00000000000..b0f3831854a --- /dev/null +++ b/doc/reference/workflow/split.dot @@ -0,0 +1,10 @@ +digraph split { + // dummy destinations as support for edges, make invisible and height 0 + a [style=invis height=0 fontsize=0] + b [style=invis height=0 fontsize=0] + c [style=invis height=0 fontsize=0] + + Activity -> a + Activity -> b + Activity -> c +} diff --git a/doc/reference/workflow/split.png b/doc/reference/workflow/split.png Binary files differnew file mode 100644 index 00000000000..d9d512cffa3 --- /dev/null +++ b/doc/reference/workflow/split.png diff --git a/doc/reference/workflow/split.svg b/doc/reference/workflow/split.svg new file mode 100644 index 00000000000..139874fdc75 --- /dev/null +++ b/doc/reference/workflow/split.svg @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.38.0 (20140413.2041) + --> +<!-- Title: split Pages: 1 --> +<svg width="206pt" height="93pt" + viewBox="0.00 0.00 206.00 92.73" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 88.7279)"> +<title>split</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-88.7279 202,-88.7279 202,4 -4,4"/> +<!-- a --> +<!-- b --> +<!-- c --> +<!-- Activity --> +<g id="node4" class="node"><title>Activity</title> +<ellipse fill="none" stroke="black" cx="99" cy="-66.7279" rx="40.0939" ry="18"/> +<text text-anchor="middle" x="99" y="-63.0279" font-family="Times,serif" font-size="14.00">Activity</text> +</g> +<!-- Activity->a --> +<g id="edge1" class="edge"><title>Activity->a</title> +<path fill="none" stroke="black" d="M80.4582,-50.6978C68.3594,-40.8903 52.7894,-28.2691 41.577,-19.1802"/> +<polygon fill="black" stroke="black" points="43.641,-16.3478 33.6687,-12.7696 39.233,-21.7857 43.641,-16.3478"/> +</g> +<!-- Activity->b --> +<g id="edge2" class="edge"><title>Activity->b</title> +<path fill="none" stroke="black" d="M99,-48.5325C99,-40.494 99,-30.9869 99,-23.1351"/> +<polygon fill="black" stroke="black" points="102.5,-22.8941 99,-12.8941 95.5001,-22.8942 102.5,-22.8941"/> +</g> +<!-- Activity->c --> +<g id="edge3" class="edge"><title>Activity->c</title> +<path fill="none" stroke="black" d="M117.542,-50.6978C129.641,-40.8903 145.211,-28.2691 156.423,-19.1802"/> +<polygon fill="black" stroke="black" points="158.767,-21.7857 164.331,-12.7696 154.359,-16.3478 158.767,-21.7857"/> +</g> +</g> +</svg> diff --git a/doc/reference/workflows.rst b/doc/reference/workflows.rst new file mode 100644 index 00000000000..210213e5f55 --- /dev/null +++ b/doc/reference/workflows.rst @@ -0,0 +1,324 @@ +.. _reference/workflows: + +Workflows +========= + +In Odoo, a workflow is a technical artefact to manage a set of "things to +do" associated to the records of a model. The workflow provides a higher-level +way to organize tasks to perform with or 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 Odoo 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. + +All in all, Odoo's workflow system provides: + +* a description of the evolution of a record (document) over time +* automatic actions based on various and flexible conditions +* management of company roles and validation steps +* management of interactions between objects +* a visual representation of document flows through their lifecycle + +For instance, a basic order could have the following flow: + +.. sphinx.ext.graphviz would be nice, but it requires ``dot`` on any machine +.. where the doc is compiled... otoh this is a pain in the ass because you +.. need 2 compilation steps (dot -> image and rst -> html) every time + +.. image:: workflow/order_0.* + :align: center + +Orders start in the *Draft* state, can be *Confirmed* by a user, and then +either shipped (*Closed*) or *Canceled*. + +A company using Odoo may want to add discount support to orders, where sales +staff has discretionary discounting powers up to 15%, but manager validation +is required for discounts beyond 15%. The workflow can be altered online to +add the relevant steps without editing Python or XML files: + +.. image:: workflow/order_1.* + :align: center + +Because Activities can perform arbitrary actions, the *Validation* can +automatically send a validation request to the relevant employee. + +.. note:: the order view needs to be modified to add an *Accept Discount* + button for managers + +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 + +.. code-block:: 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 +Odoo 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". + +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, Odoo 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 Odoo 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``. Odoo 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, Odoo evaluates its transition to reach +the next activity in the flow. + +However if an activity has more than one transition, Odoo must decide which +activity or activities to follow. + +.. image:: workflow/split.* + :align: center + +This choice is controlled by the ``split_mode`` attribute: + +``XOR`` (default) + By default, Odoo will use the first transition (in ``sequence`` order) + whose condition is satisfied. All other transitions are ignored. +``OR`` + In ``OR`` mode, all transitions with a satisfied condition are traversed + simultanously. Transitions not yet valid will be ignored, even if they + become valid later. +``AND`` + In ``AND`` mode, Odoo will wait until *all* transitions are satisfied, and + will traverse all of them (much like the ``OR`` mode). + +Both ``OR`` and ``AND`` mode will lead to activities being active in the same +workflow. + +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. + +.. image:: workflow/join.* + :align: center + +The ``join_mode`` attribute controls that behavior: + +``XOR`` (default) + Any incoming transition enables the activity and starts its processing. +``AND`` + The activity is enabled and processed only once *all* incoming transitions + have been traversed. + +Kinds +''''' + +An activity's kind defines the type of work an activity can perform. + +Dummy (``dummy``, default) + Do nothing at all, or call a server action. Often used as dispatch or + gather "hubs" for transitions. +Function (``function``) + Run some python code, execute a server action. +Stop all (``stopall``) + Completely stops the workflow instance and marks it as completed. +Subflow (``subflow``) + Starts executing an other workflow, once that workflow is completed the + activity is done processing. + + 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. + + +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 Odoo, 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 Odoo ``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:: triggers are not re-installed whenever the transition is re-tried. |
