mirror of https://github.com/OCA/web.git
parent
b97fb4aa2c
commit
c6d9bf6987
|
@ -14,13 +14,13 @@ Web timeline
|
||||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
:alt: License: AGPL-3
|
:alt: License: AGPL-3
|
||||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
|
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
|
||||||
:target: https://github.com/OCA/web/tree/15.0/web_timeline
|
:target: https://github.com/OCA/web/tree/16.0/web_timeline
|
||||||
:alt: OCA/web
|
:alt: OCA/web
|
||||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||||
:target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_timeline
|
:target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_timeline
|
||||||
:alt: Translate me on Weblate
|
:alt: Translate me on Weblate
|
||||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||||
:target: https://runbot.odoo-community.org/runbot/162/15.0
|
:target: https://runbot.odoo-community.org/runbot/162/16.0
|
||||||
:alt: Try me on Runbot
|
:alt: Try me on Runbot
|
||||||
|
|
||||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||||
|
@ -91,25 +91,50 @@ Example:
|
||||||
<timeline date_start="date_assign"
|
<timeline date_start="date_assign"
|
||||||
date_stop="date_end"
|
date_stop="date_end"
|
||||||
string="Tasks"
|
string="Tasks"
|
||||||
default_group_by="user_id"
|
default_group_by="project_id"
|
||||||
event_open_popup="true"
|
event_open_popup="true"
|
||||||
zoomKey="ctrlKey"
|
colors="white: user_ids == []; #2ecb71: kanban_state == 'done'; #ec7063: kanban_state == 'blocked'"
|
||||||
colors="#ec7063:user_id == false;#2ecb71:kanban_state=='done';"
|
dependency_arrow="depend_on_ids"
|
||||||
dependency_arrow="task_dependency_ids">
|
>
|
||||||
<field name="user_id"/>
|
<field name="user_ids" />
|
||||||
|
<field name="planned_hours" />
|
||||||
<templates>
|
<templates>
|
||||||
<div t-name="timeline-item">
|
<t t-name="timeline-item">
|
||||||
<div t-esc="record.display_name"/>
|
<div class="o_project_timeline_item">
|
||||||
Assigned to:
|
<t t-foreach="record.user_ids" t-as="user">
|
||||||
<span t-esc="record.user_id[1]"/>
|
<img
|
||||||
|
t-if="record.user_ids"
|
||||||
|
t-attf-src="/web/image/res.users/#{user}/image_128/16x16"
|
||||||
|
t-att-title="record.user"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
class="mr8"
|
||||||
|
alt="User"
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
<span name="display_name">
|
||||||
|
<t t-esc="record.display_name" />
|
||||||
|
</span>
|
||||||
|
<small
|
||||||
|
name="planned_hours"
|
||||||
|
class="text-info ml4"
|
||||||
|
t-if="record.planned_hours"
|
||||||
|
>
|
||||||
|
<t
|
||||||
|
t-esc="field_utils.format.float_time(record.planned_hours)"
|
||||||
|
/>
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
</t>
|
||||||
</templates>
|
</templates>
|
||||||
</timeline>
|
</timeline>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="project.action_view_task" model="ir.actions.act_window">
|
<record id="project.action_view_task" model="ir.actions.act_window">
|
||||||
<field name="view_mode">kanban,tree,form,calendar,gantt,timeline,graph</field>
|
<field
|
||||||
|
name="view_mode"
|
||||||
|
>kanban,tree,form,calendar,timeline,pivot,graph,activity</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
||||||
|
@ -165,7 +190,7 @@ Bug Tracker
|
||||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
|
||||||
In case of trouble, please check there if your issue has already been reported.
|
In case of trouble, please check there if your issue has already been reported.
|
||||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||||
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_timeline%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_timeline%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||||
|
|
||||||
Do not contact contributors directly about support or help with technical issues.
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
|
|
||||||
|
@ -188,7 +213,6 @@ Contributors
|
||||||
* Adrien Peiffer <adrien.peiffer@acsone.eu>
|
* Adrien Peiffer <adrien.peiffer@acsone.eu>
|
||||||
* Leonardo Donelli <donelli@webmonks.it>
|
* Leonardo Donelli <donelli@webmonks.it>
|
||||||
* Adrien Didenot <adrien.didenot@horanet.com>
|
* Adrien Didenot <adrien.didenot@horanet.com>
|
||||||
* Dennis Sluijk <d.sluijk@onestein.nl>
|
|
||||||
* Thong Nguyen Van <thongnv@trobz.com>
|
* Thong Nguyen Van <thongnv@trobz.com>
|
||||||
* Murtaza Mithaiwala <mmithaiwala@opensourceintegrators.com>
|
* Murtaza Mithaiwala <mmithaiwala@opensourceintegrators.com>
|
||||||
* Ammar Officewala <aofficewala@opensourceintegrators.com>
|
* Ammar Officewala <aofficewala@opensourceintegrators.com>
|
||||||
|
@ -197,6 +221,9 @@ Contributors
|
||||||
* Pedro M. Baeza
|
* Pedro M. Baeza
|
||||||
* Alexandre Díaz
|
* Alexandre Díaz
|
||||||
* César A. Sánchez
|
* César A. Sánchez
|
||||||
|
* `Onestein <https://www.onestein.nl>`_:
|
||||||
|
* Dennis Sluijk <d.sluijk@onestein.nl>
|
||||||
|
* Anjeel Haria
|
||||||
|
|
||||||
Maintainers
|
Maintainers
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
@ -219,6 +246,6 @@ Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|
||||||
|
|
||||||
|maintainer-tarteo|
|
|maintainer-tarteo|
|
||||||
|
|
||||||
This module is part of the `OCA/web <https://github.com/OCA/web/tree/15.0/web_timeline>`_ project on GitHub.
|
This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_timeline>`_ project on GitHub.
|
||||||
|
|
||||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{
|
{
|
||||||
"name": "Web timeline",
|
"name": "Web timeline",
|
||||||
"summary": "Interactive visualization chart to show events in time",
|
"summary": "Interactive visualization chart to show events in time",
|
||||||
"version": "15.0.1.0.1",
|
"version": "16.0.1.0.0",
|
||||||
"development_status": "Production/Stable",
|
"development_status": "Production/Stable",
|
||||||
"author": "ACSONE SA/NV, "
|
"author": "ACSONE SA/NV, "
|
||||||
"Tecnativa, "
|
"Tecnativa, "
|
||||||
|
@ -25,11 +25,9 @@
|
||||||
"web_timeline/static/src/scss/web_timeline.scss",
|
"web_timeline/static/src/scss/web_timeline.scss",
|
||||||
"web_timeline/static/src/js/timeline_view.js",
|
"web_timeline/static/src/js/timeline_view.js",
|
||||||
"web_timeline/static/src/js/timeline_renderer.js",
|
"web_timeline/static/src/js/timeline_renderer.js",
|
||||||
"web_timeline/static/src/js/timeline_controller.js",
|
"web_timeline/static/src/js/timeline_controller.esm.js",
|
||||||
"web_timeline/static/src/js/timeline_model.js",
|
"web_timeline/static/src/js/timeline_model.js",
|
||||||
"web_timeline/static/src/js/timeline_canvas.js",
|
"web_timeline/static/src/js/timeline_canvas.js",
|
||||||
],
|
|
||||||
"web.assets_qweb": [
|
|
||||||
"web_timeline/static/src/xml/web_timeline.xml",
|
"web_timeline/static/src/xml/web_timeline.xml",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Odoo Server 15.0\n"
|
"Project-Id-Version: Odoo Server 16.0\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
|
@ -14,42 +14,42 @@ msgstr ""
|
||||||
"Plural-Forms: \n"
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/js/timeline_renderer.js:0
|
#: code:addons/web_timeline/static/src/js/timeline_renderer.js:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<b>UNASSIGNED</b>"
|
msgid "<b>UNASSIGNED</b>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/js/timeline_controller.js:0
|
#: code:addons/web_timeline/static/src/js/timeline_controller.esm.js:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Are you sure you want to delete this record?"
|
msgid "Are you sure you want to delete this record?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Day"
|
msgid "Day"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Month"
|
msgid "Month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/js/timeline_renderer.js:0
|
#: code:addons/web_timeline/static/src/js/timeline_renderer.js:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Template \"timeline-item\" not present in timeline view definition."
|
msgid "Template \"timeline-item\" not present in timeline view definition."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/js/timeline_view.js:0
|
#: code:addons/web_timeline/static/src/js/timeline_view.js:0
|
||||||
#: model:ir.model.fields.selection,name:web_timeline.selection__ir_ui_view__type__timeline
|
#: model:ir.model.fields.selection,name:web_timeline.selection__ir_ui_view__type__timeline
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -57,14 +57,14 @@ msgid "Timeline"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/js/timeline_renderer.js:0
|
#: code:addons/web_timeline/static/src/js/timeline_renderer.js:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Timeline view has not defined 'date_start' attribute."
|
msgid "Timeline view has not defined 'date_start' attribute."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Today"
|
msgid "Today"
|
||||||
|
@ -81,21 +81,21 @@ msgid "View Type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/js/timeline_controller.js:0
|
#: code:addons/web_timeline/static/src/js/timeline_controller.esm.js:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Week"
|
msgid "Week"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_timeline
|
#. module: web_timeline
|
||||||
#. openerp-web
|
#. odoo-javascript
|
||||||
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
#: code:addons/web_timeline/static/src/xml/web_timeline.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Year"
|
msgid "Year"
|
||||||
|
|
|
@ -51,24 +51,49 @@ Example:
|
||||||
<timeline date_start="date_assign"
|
<timeline date_start="date_assign"
|
||||||
date_stop="date_end"
|
date_stop="date_end"
|
||||||
string="Tasks"
|
string="Tasks"
|
||||||
default_group_by="user_id"
|
default_group_by="project_id"
|
||||||
event_open_popup="true"
|
event_open_popup="true"
|
||||||
zoomKey="ctrlKey"
|
colors="white: user_ids == []; #2ecb71: kanban_state == 'done'; #ec7063: kanban_state == 'blocked'"
|
||||||
colors="#ec7063:user_id == false;#2ecb71:kanban_state=='done';"
|
dependency_arrow="depend_on_ids"
|
||||||
dependency_arrow="task_dependency_ids">
|
>
|
||||||
<field name="user_id"/>
|
<field name="user_ids" />
|
||||||
|
<field name="planned_hours" />
|
||||||
<templates>
|
<templates>
|
||||||
<div t-name="timeline-item">
|
<t t-name="timeline-item">
|
||||||
<div t-esc="record.display_name"/>
|
<div class="o_project_timeline_item">
|
||||||
Assigned to:
|
<t t-foreach="record.user_ids" t-as="user">
|
||||||
<span t-esc="record.user_id[1]"/>
|
<img
|
||||||
|
t-if="record.user_ids"
|
||||||
|
t-attf-src="/web/image/res.users/#{user}/image_128/16x16"
|
||||||
|
t-att-title="record.user"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
class="mr8"
|
||||||
|
alt="User"
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
<span name="display_name">
|
||||||
|
<t t-esc="record.display_name" />
|
||||||
|
</span>
|
||||||
|
<small
|
||||||
|
name="planned_hours"
|
||||||
|
class="text-info ml4"
|
||||||
|
t-if="record.planned_hours"
|
||||||
|
>
|
||||||
|
<t
|
||||||
|
t-esc="field_utils.format.float_time(record.planned_hours)"
|
||||||
|
/>
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
</t>
|
||||||
</templates>
|
</templates>
|
||||||
</timeline>
|
</timeline>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="project.action_view_task" model="ir.actions.act_window">
|
<record id="project.action_view_task" model="ir.actions.act_window">
|
||||||
<field name="view_mode">kanban,tree,form,calendar,gantt,timeline,graph</field>
|
<field
|
||||||
|
name="view_mode"
|
||||||
|
>kanban,tree,form,calendar,timeline,pivot,graph,activity</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
* Adrien Peiffer <adrien.peiffer@acsone.eu>
|
* Adrien Peiffer <adrien.peiffer@acsone.eu>
|
||||||
* Leonardo Donelli <donelli@webmonks.it>
|
* Leonardo Donelli <donelli@webmonks.it>
|
||||||
* Adrien Didenot <adrien.didenot@horanet.com>
|
* Adrien Didenot <adrien.didenot@horanet.com>
|
||||||
* Dennis Sluijk <d.sluijk@onestein.nl>
|
|
||||||
* Thong Nguyen Van <thongnv@trobz.com>
|
* Thong Nguyen Van <thongnv@trobz.com>
|
||||||
* Murtaza Mithaiwala <mmithaiwala@opensourceintegrators.com>
|
* Murtaza Mithaiwala <mmithaiwala@opensourceintegrators.com>
|
||||||
* Ammar Officewala <aofficewala@opensourceintegrators.com>
|
* Ammar Officewala <aofficewala@opensourceintegrators.com>
|
||||||
|
@ -11,3 +10,6 @@
|
||||||
* Pedro M. Baeza
|
* Pedro M. Baeza
|
||||||
* Alexandre Díaz
|
* Alexandre Díaz
|
||||||
* César A. Sánchez
|
* César A. Sánchez
|
||||||
|
* `Onestein <https://www.onestein.nl>`_:
|
||||||
|
* Dennis Sluijk <d.sluijk@onestein.nl>
|
||||||
|
* Anjeel Haria
|
||||||
|
|
|
@ -367,7 +367,7 @@ ul.auto-toc {
|
||||||
!! This file is generated by oca-gen-addon-readme !!
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
!! changes will be overwritten. !!
|
!! changes will be overwritten. !!
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_timeline"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_timeline"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_timeline"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_timeline"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/16.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||||
<p>Define a new view displaying events in an interactive visualization chart.</p>
|
<p>Define a new view displaying events in an interactive visualization chart.</p>
|
||||||
<p>The widget is based on the external library
|
<p>The widget is based on the external library
|
||||||
<a class="reference external" href="https://visjs.github.io/vis-timeline/examples/timeline">https://visjs.github.io/vis-timeline/examples/timeline</a></p>
|
<a class="reference external" href="https://visjs.github.io/vis-timeline/examples/timeline">https://visjs.github.io/vis-timeline/examples/timeline</a></p>
|
||||||
|
@ -470,25 +470,50 @@ These are the variables available in template rendering:</p>
|
||||||
<span class="nt"><timeline</span> <span class="na">date_start=</span><span class="s">"date_assign"</span>
|
<span class="nt"><timeline</span> <span class="na">date_start=</span><span class="s">"date_assign"</span>
|
||||||
<span class="na">date_stop=</span><span class="s">"date_end"</span>
|
<span class="na">date_stop=</span><span class="s">"date_end"</span>
|
||||||
<span class="na">string=</span><span class="s">"Tasks"</span>
|
<span class="na">string=</span><span class="s">"Tasks"</span>
|
||||||
<span class="na">default_group_by=</span><span class="s">"user_id"</span>
|
<span class="na">default_group_by=</span><span class="s">"project_id"</span>
|
||||||
<span class="na">event_open_popup=</span><span class="s">"true"</span>
|
<span class="na">event_open_popup=</span><span class="s">"true"</span>
|
||||||
<span class="na">zoomKey=</span><span class="s">"ctrlKey"</span>
|
<span class="na">colors=</span><span class="s">"white: user_ids == []; #2ecb71: kanban_state == 'done'; #ec7063: kanban_state == 'blocked'"</span>
|
||||||
<span class="na">colors=</span><span class="s">"#ec7063:user_id == false;#2ecb71:kanban_state=='done';"</span>
|
<span class="na">dependency_arrow=</span><span class="s">"depend_on_ids"</span>
|
||||||
<span class="na">dependency_arrow=</span><span class="s">"task_dependency_ids"</span><span class="nt">></span>
|
<span class="nt">></span>
|
||||||
<span class="nt"><field</span> <span class="na">name=</span><span class="s">"user_id"</span><span class="nt">/></span>
|
<span class="nt"><field</span> <span class="na">name=</span><span class="s">"user_ids"</span> <span class="nt">/></span>
|
||||||
|
<span class="nt"><field</span> <span class="na">name=</span><span class="s">"planned_hours"</span> <span class="nt">/></span>
|
||||||
<span class="nt"><templates></span>
|
<span class="nt"><templates></span>
|
||||||
<span class="nt"><div</span> <span class="na">t-name=</span><span class="s">"timeline-item"</span><span class="nt">></span>
|
<span class="nt"><t</span> <span class="na">t-name=</span><span class="s">"timeline-item"</span><span class="nt">></span>
|
||||||
<span class="nt"><div</span> <span class="na">t-esc=</span><span class="s">"record.display_name"</span><span class="nt">/></span>
|
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"o_project_timeline_item"</span><span class="nt">></span>
|
||||||
Assigned to:
|
<span class="nt"><t</span> <span class="na">t-foreach=</span><span class="s">"record.user_ids"</span> <span class="na">t-as=</span><span class="s">"user"</span><span class="nt">></span>
|
||||||
<span class="nt"><span</span> <span class="na">t-esc=</span><span class="s">"record.user_id[1]"</span><span class="nt">/></span>
|
<span class="nt"><img</span>
|
||||||
|
<span class="na">t-if=</span><span class="s">"record.user_ids"</span>
|
||||||
|
<span class="na">t-attf-src=</span><span class="s">"/web/image/res.users/#{user}/image_128/16x16"</span>
|
||||||
|
<span class="na">t-att-title=</span><span class="s">"record.user"</span>
|
||||||
|
<span class="na">width=</span><span class="s">"16"</span>
|
||||||
|
<span class="na">height=</span><span class="s">"16"</span>
|
||||||
|
<span class="na">class=</span><span class="s">"mr8"</span>
|
||||||
|
<span class="na">alt=</span><span class="s">"User"</span>
|
||||||
|
<span class="nt">/></span>
|
||||||
|
<span class="nt"></t></span>
|
||||||
|
<span class="nt"><span</span> <span class="na">name=</span><span class="s">"display_name"</span><span class="nt">></span>
|
||||||
|
<span class="nt"><t</span> <span class="na">t-esc=</span><span class="s">"record.display_name"</span> <span class="nt">/></span>
|
||||||
|
<span class="nt"></span></span>
|
||||||
|
<span class="nt"><small</span>
|
||||||
|
<span class="na">name=</span><span class="s">"planned_hours"</span>
|
||||||
|
<span class="na">class=</span><span class="s">"text-info ml4"</span>
|
||||||
|
<span class="na">t-if=</span><span class="s">"record.planned_hours"</span>
|
||||||
|
<span class="nt">></span>
|
||||||
|
<span class="nt"><t</span>
|
||||||
|
<span class="na">t-esc=</span><span class="s">"field_utils.format.float_time(record.planned_hours)"</span>
|
||||||
|
<span class="nt">/></span>
|
||||||
|
<span class="nt"></small></span>
|
||||||
<span class="nt"></div></span>
|
<span class="nt"></div></span>
|
||||||
|
<span class="nt"></t></span>
|
||||||
<span class="nt"></templates></span>
|
<span class="nt"></templates></span>
|
||||||
<span class="nt"></timeline></span>
|
<span class="nt"></timeline></span>
|
||||||
<span class="nt"></field></span>
|
<span class="nt"></field></span>
|
||||||
<span class="nt"></record></span>
|
<span class="nt"></record></span>
|
||||||
|
|
||||||
<span class="nt"><record</span> <span class="na">id=</span><span class="s">"project.action_view_task"</span> <span class="na">model=</span><span class="s">"ir.actions.act_window"</span><span class="nt">></span>
|
<span class="nt"><record</span> <span class="na">id=</span><span class="s">"project.action_view_task"</span> <span class="na">model=</span><span class="s">"ir.actions.act_window"</span><span class="nt">></span>
|
||||||
<span class="nt"><field</span> <span class="na">name=</span><span class="s">"view_mode"</span><span class="nt">></span>kanban,tree,form,calendar,gantt,timeline,graph<span class="nt"></field></span>
|
<span class="nt"><field</span>
|
||||||
|
<span class="na">name=</span><span class="s">"view_mode"</span>
|
||||||
|
<span class="nt">></span>kanban,tree,form,calendar,timeline,pivot,graph,activity<span class="nt"></field></span>
|
||||||
<span class="nt"></record></span>
|
<span class="nt"></record></span>
|
||||||
<span class="nt"></odoo></span>
|
<span class="nt"></odoo></span>
|
||||||
</pre>
|
</pre>
|
||||||
|
@ -539,7 +564,7 @@ the value according the group of the dragged item.</li>
|
||||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
|
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
|
||||||
In case of trouble, please check there if your issue has already been reported.
|
In case of trouble, please check there if your issue has already been reported.
|
||||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||||
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_timeline%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_timeline%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="credits">
|
<div class="section" id="credits">
|
||||||
|
@ -561,7 +586,6 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
||||||
<li>Adrien Peiffer <<a class="reference external" href="mailto:adrien.peiffer@acsone.eu">adrien.peiffer@acsone.eu</a>></li>
|
<li>Adrien Peiffer <<a class="reference external" href="mailto:adrien.peiffer@acsone.eu">adrien.peiffer@acsone.eu</a>></li>
|
||||||
<li>Leonardo Donelli <<a class="reference external" href="mailto:donelli@webmonks.it">donelli@webmonks.it</a>></li>
|
<li>Leonardo Donelli <<a class="reference external" href="mailto:donelli@webmonks.it">donelli@webmonks.it</a>></li>
|
||||||
<li>Adrien Didenot <<a class="reference external" href="mailto:adrien.didenot@horanet.com">adrien.didenot@horanet.com</a>></li>
|
<li>Adrien Didenot <<a class="reference external" href="mailto:adrien.didenot@horanet.com">adrien.didenot@horanet.com</a>></li>
|
||||||
<li>Dennis Sluijk <<a class="reference external" href="mailto:d.sluijk@onestein.nl">d.sluijk@onestein.nl</a>></li>
|
|
||||||
<li>Thong Nguyen Van <<a class="reference external" href="mailto:thongnv@trobz.com">thongnv@trobz.com</a>></li>
|
<li>Thong Nguyen Van <<a class="reference external" href="mailto:thongnv@trobz.com">thongnv@trobz.com</a>></li>
|
||||||
<li>Murtaza Mithaiwala <<a class="reference external" href="mailto:mmithaiwala@opensourceintegrators.com">mmithaiwala@opensourceintegrators.com</a>></li>
|
<li>Murtaza Mithaiwala <<a class="reference external" href="mailto:mmithaiwala@opensourceintegrators.com">mmithaiwala@opensourceintegrators.com</a>></li>
|
||||||
<li>Ammar Officewala <<a class="reference external" href="mailto:aofficewala@opensourceintegrators.com">aofficewala@opensourceintegrators.com</a>></li>
|
<li>Ammar Officewala <<a class="reference external" href="mailto:aofficewala@opensourceintegrators.com">aofficewala@opensourceintegrators.com</a>></li>
|
||||||
|
@ -571,6 +595,15 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
||||||
<li>César A. Sánchez</li>
|
<li>César A. Sánchez</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li><dl class="first docutils">
|
||||||
|
<dt><a class="reference external" href="https://www.onestein.nl">Onestein</a>:</dt>
|
||||||
|
<dd><ul class="first last">
|
||||||
|
<li>Dennis Sluijk <<a class="reference external" href="mailto:d.sluijk@onestein.nl">d.sluijk@onestein.nl</a>></li>
|
||||||
|
<li>Anjeel Haria</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="maintainers">
|
<div class="section" id="maintainers">
|
||||||
|
@ -582,7 +615,7 @@ mission is to support the collaborative development of Odoo features and
|
||||||
promote its widespread use.</p>
|
promote its widespread use.</p>
|
||||||
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
|
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
|
||||||
<p><a class="reference external" href="https://github.com/tarteo"><img alt="tarteo" src="https://github.com/tarteo.png?size=40px" /></a></p>
|
<p><a class="reference external" href="https://github.com/tarteo"><img alt="tarteo" src="https://github.com/tarteo.png?size=40px" /></a></p>
|
||||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_timeline">OCA/web</a> project on GitHub.</p>
|
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_timeline">OCA/web</a> project on GitHub.</p>
|
||||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50109,4 +50109,3 @@
|
||||||
Object.defineProperty(exports, '__esModule', { value: true });
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
}));
|
}));
|
||||||
//# sourceMappingURL=vis-timeline-graph2d.js.map
|
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
/** @odoo-module alias=web_timeline.TimelineController **/
|
||||||
|
/* Copyright 2023 Onestein - Anjeel Haria
|
||||||
|
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
|
||||||
|
import AbstractController from "web.AbstractController";
|
||||||
|
import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog";
|
||||||
|
import time from "web.time";
|
||||||
|
import core from "web.core";
|
||||||
|
import Dialog from "web.Dialog";
|
||||||
|
var _t = core._t;
|
||||||
|
import {Component} from "@odoo/owl";
|
||||||
|
|
||||||
|
export default AbstractController.extend({
|
||||||
|
custom_events: _.extend({}, AbstractController.prototype.custom_events, {
|
||||||
|
onGroupClick: "_onGroupClick",
|
||||||
|
onUpdate: "_onUpdate",
|
||||||
|
onRemove: "_onRemove",
|
||||||
|
onMove: "_onMove",
|
||||||
|
onAdd: "_onAdd",
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
init: function (parent, model, renderer, params) {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.open_popup_action = params.open_popup_action;
|
||||||
|
this.date_start = params.date_start;
|
||||||
|
this.date_stop = params.date_stop;
|
||||||
|
this.date_delay = params.date_delay;
|
||||||
|
this.context = params.actionContext;
|
||||||
|
this.moveQueue = [];
|
||||||
|
this.debouncedInternalMove = _.debounce(this.internalMove, 0);
|
||||||
|
},
|
||||||
|
on_detach_callback() {
|
||||||
|
if (this.Dialog) {
|
||||||
|
this.Dialog();
|
||||||
|
this.Dialog = undefined;
|
||||||
|
}
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
update: function (params, options) {
|
||||||
|
const res = this._super.apply(this, arguments);
|
||||||
|
if (_.isEmpty(params)) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
const defaults = _.defaults({}, options, {
|
||||||
|
adjust_window: true,
|
||||||
|
});
|
||||||
|
const domains = params.domain || this.renderer.last_domains || [];
|
||||||
|
const contexts = params.context || [];
|
||||||
|
const group_bys = params.groupBy || this.renderer.last_group_bys || [];
|
||||||
|
this.last_domains = domains;
|
||||||
|
this.last_contexts = contexts;
|
||||||
|
// Select the group by
|
||||||
|
let n_group_bys = group_bys;
|
||||||
|
if (!n_group_bys.length && this.renderer.arch.attrs.default_group_by) {
|
||||||
|
n_group_bys = this.renderer.arch.attrs.default_group_by.split(",");
|
||||||
|
}
|
||||||
|
this.renderer.last_group_bys = n_group_bys;
|
||||||
|
this.renderer.last_domains = domains;
|
||||||
|
|
||||||
|
let fields = this.renderer.fieldNames;
|
||||||
|
fields = _.uniq(fields.concat(n_group_bys));
|
||||||
|
$.when(
|
||||||
|
res,
|
||||||
|
this._rpc({
|
||||||
|
model: this.model.modelName,
|
||||||
|
method: "search_read",
|
||||||
|
kwargs: {
|
||||||
|
fields: fields,
|
||||||
|
domain: domains,
|
||||||
|
order: [{name: this.renderer.arch.attrs.default_group_by}],
|
||||||
|
},
|
||||||
|
context: this.getSession().user_context,
|
||||||
|
}).then((data) =>
|
||||||
|
this.renderer.on_data_loaded(data, n_group_bys, defaults.adjust_window)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets triggered when a group in the timeline is
|
||||||
|
* clicked (by the TimelineRenderer).
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {EventObject} event
|
||||||
|
* @returns {jQuery.Deferred}
|
||||||
|
*/
|
||||||
|
_onGroupClick: function (event) {
|
||||||
|
const groupField = this.renderer.last_group_bys[0];
|
||||||
|
return this.do_action({
|
||||||
|
type: "ir.actions.act_window",
|
||||||
|
res_model: this.renderer.fields[groupField].relation,
|
||||||
|
res_id: event.data.item.group,
|
||||||
|
target: "new",
|
||||||
|
views: [[false, "form"]],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a form view of a clicked timeline
|
||||||
|
* item (triggered by the TimelineRenderer).
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {EventObject} event
|
||||||
|
*/
|
||||||
|
_onUpdate: function (event) {
|
||||||
|
this.renderer = event.data.renderer;
|
||||||
|
const rights = event.data.rights;
|
||||||
|
const item = event.data.item;
|
||||||
|
const id = Number(item.evt.id) || item.evt.id;
|
||||||
|
const title = item.evt.__name;
|
||||||
|
if (this.open_popup_action) {
|
||||||
|
this.Dialog = Component.env.services.dialog.add(
|
||||||
|
FormViewDialog,
|
||||||
|
{
|
||||||
|
resId: id,
|
||||||
|
context: this.getSession().user_context,
|
||||||
|
title: title,
|
||||||
|
onRecordSaved: () => this.write_completed(),
|
||||||
|
resModel: this.model.modelName,
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let mode = "readonly";
|
||||||
|
if (rights.write) {
|
||||||
|
mode = "edit";
|
||||||
|
}
|
||||||
|
this.trigger_up("switch_view", {
|
||||||
|
view_type: "form",
|
||||||
|
res_id: id,
|
||||||
|
mode: mode,
|
||||||
|
model: this.model.modelName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets triggered when a timeline item is
|
||||||
|
* moved (triggered by the TimelineRenderer).
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {EventObject} event
|
||||||
|
*/
|
||||||
|
_onMove: function (event) {
|
||||||
|
const item = event.data.item;
|
||||||
|
const fields = this.renderer.fields;
|
||||||
|
const event_start = item.start;
|
||||||
|
const event_end = item.end;
|
||||||
|
let group = false;
|
||||||
|
if (item.group !== -1) {
|
||||||
|
group = item.group;
|
||||||
|
}
|
||||||
|
const data = {};
|
||||||
|
// In case of a move event, the date_delay stay the same,
|
||||||
|
// only date_start and stop must be updated
|
||||||
|
data[this.date_start] = time.auto_date_to_str(
|
||||||
|
event_start,
|
||||||
|
fields[this.date_start].type
|
||||||
|
);
|
||||||
|
if (this.date_stop) {
|
||||||
|
// In case of instantaneous event, item.end is not defined
|
||||||
|
if (event_end) {
|
||||||
|
data[this.date_stop] = time.auto_date_to_str(
|
||||||
|
event_end,
|
||||||
|
fields[this.date_stop].type
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
data[this.date_stop] = data[this.date_start];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.date_delay && event_end) {
|
||||||
|
const diff_seconds = Math.round(
|
||||||
|
(event_end.getTime() - event_start.getTime()) / 1000
|
||||||
|
);
|
||||||
|
data[this.date_delay] = diff_seconds / 3600;
|
||||||
|
}
|
||||||
|
const grouped_field = this.renderer.last_group_bys[0];
|
||||||
|
this._rpc({
|
||||||
|
model: this.modelName,
|
||||||
|
method: "fields_get",
|
||||||
|
args: [grouped_field],
|
||||||
|
context: this.getSession().user_context,
|
||||||
|
}).then(async (fields_processed) => {
|
||||||
|
if (
|
||||||
|
this.renderer.last_group_bys &&
|
||||||
|
this.renderer.last_group_bys instanceof Array &&
|
||||||
|
fields_processed[grouped_field].type !== "many2many"
|
||||||
|
) {
|
||||||
|
data[this.renderer.last_group_bys[0]] = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moveQueue.push({
|
||||||
|
id: event.data.item.id,
|
||||||
|
data: data,
|
||||||
|
event: event,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.debouncedInternalMove();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write enqueued moves to Odoo. After all writes are finished it updates
|
||||||
|
* the view once (prevents flickering of the view when multiple timeline items
|
||||||
|
* are moved at once).
|
||||||
|
*
|
||||||
|
* @returns {jQuery.Deferred}
|
||||||
|
*/
|
||||||
|
internalMove: function () {
|
||||||
|
const queues = this.moveQueue.slice();
|
||||||
|
this.moveQueue = [];
|
||||||
|
const defers = [];
|
||||||
|
for (const item of queues) {
|
||||||
|
defers.push(
|
||||||
|
this._rpc({
|
||||||
|
model: this.model.modelName,
|
||||||
|
method: "write",
|
||||||
|
args: [[item.event.data.item.id], item.data],
|
||||||
|
context: this.getSession().user_context,
|
||||||
|
}).then(() => {
|
||||||
|
item.event.data.callback(item.event.data.item);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $.when.apply($, defers).done(() => {
|
||||||
|
this.write_completed({
|
||||||
|
adjust_window: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when a timeline item gets removed from the view.
|
||||||
|
* Requires user confirmation before it gets actually deleted.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {EventObject} event
|
||||||
|
* @returns {jQuery.Deferred}
|
||||||
|
*/
|
||||||
|
_onRemove: function (event) {
|
||||||
|
var def = $.Deferred();
|
||||||
|
|
||||||
|
Dialog.confirm(this, _t("Are you sure you want to delete this record?"), {
|
||||||
|
title: _t("Warning"),
|
||||||
|
confirm_callback: () => {
|
||||||
|
this.remove_completed(event).then(def.resolve.bind(def));
|
||||||
|
},
|
||||||
|
cancel_callback: def.resolve.bind(def),
|
||||||
|
});
|
||||||
|
|
||||||
|
return def;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when a timeline item gets added and opens a form view.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {EventObject} event
|
||||||
|
* @returns {dialogs.FormViewDialog}
|
||||||
|
*/
|
||||||
|
_onAdd: function (event) {
|
||||||
|
const item = event.data.item;
|
||||||
|
// Initialize default values for creation
|
||||||
|
const default_context = {};
|
||||||
|
default_context["default_".concat(this.date_start)] = item.start;
|
||||||
|
if (this.date_delay) {
|
||||||
|
default_context["default_".concat(this.date_delay)] = 1;
|
||||||
|
}
|
||||||
|
if (this.date_start) {
|
||||||
|
default_context["default_".concat(this.date_start)] = moment(item.start)
|
||||||
|
.utc()
|
||||||
|
.format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
}
|
||||||
|
if (this.date_stop && item.end) {
|
||||||
|
default_context["default_".concat(this.date_stop)] = moment(item.end)
|
||||||
|
.utc()
|
||||||
|
.format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
}
|
||||||
|
if (item.group > 0) {
|
||||||
|
default_context["default_".concat(this.renderer.last_group_bys[0])] =
|
||||||
|
item.group;
|
||||||
|
}
|
||||||
|
// Show popup
|
||||||
|
this.Dialog = Component.env.services.dialog.add(
|
||||||
|
FormViewDialog,
|
||||||
|
{
|
||||||
|
resId: null,
|
||||||
|
context: _.extend(default_context, this.context),
|
||||||
|
onRecordSaved: (record) => this.create_completed([record.res_id]),
|
||||||
|
resModel: this.model.modelName,
|
||||||
|
},
|
||||||
|
{onClose: () => event.data.callback()}
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered upon completion of a new record.
|
||||||
|
* Updates the timeline view with the new record.
|
||||||
|
*
|
||||||
|
* @param {RecordId} id
|
||||||
|
* @returns {jQuery.Deferred}
|
||||||
|
*/
|
||||||
|
create_completed: function (id) {
|
||||||
|
return this._rpc({
|
||||||
|
model: this.model.modelName,
|
||||||
|
method: "read",
|
||||||
|
args: [id, this.model.fieldNames],
|
||||||
|
context: this.context,
|
||||||
|
}).then((records) => {
|
||||||
|
var new_event = this.renderer.event_data_transform(records[0]);
|
||||||
|
var items = this.renderer.timeline.itemsData;
|
||||||
|
items.add(new_event);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered upon completion of writing a record.
|
||||||
|
* @param {ControllerOptions} options
|
||||||
|
*/
|
||||||
|
write_completed: function (options) {
|
||||||
|
const params = {
|
||||||
|
domain: this.renderer.last_domains,
|
||||||
|
context: this.context,
|
||||||
|
groupBy: this.renderer.last_group_bys,
|
||||||
|
};
|
||||||
|
this.update(params, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered upon confirm of removing a record.
|
||||||
|
* @param {EventObject} event
|
||||||
|
* @returns {jQuery.Deferred}
|
||||||
|
*/
|
||||||
|
remove_completed: function (event) {
|
||||||
|
return this._rpc({
|
||||||
|
model: this.modelName,
|
||||||
|
method: "unlink",
|
||||||
|
args: [[event.data.item.id]],
|
||||||
|
context: this.getSession().user_context,
|
||||||
|
}).then(() => {
|
||||||
|
let unlink_index = false;
|
||||||
|
for (var i = 0; i < this.model.data.data.length; i++) {
|
||||||
|
if (this.model.data.data[i].id === event.data.item.id) {
|
||||||
|
unlink_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isNaN(unlink_index)) {
|
||||||
|
this.model.data.data.splice(unlink_index, 1);
|
||||||
|
}
|
||||||
|
event.data.callback(event.data.item);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,364 +0,0 @@
|
||||||
odoo.define("web_timeline.TimelineController", function (require) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const AbstractController = require("web.AbstractController");
|
|
||||||
const dialogs = require("web.view_dialogs");
|
|
||||||
const core = require("web.core");
|
|
||||||
const time = require("web.time");
|
|
||||||
const Dialog = require("web.Dialog");
|
|
||||||
|
|
||||||
const _t = core._t;
|
|
||||||
|
|
||||||
const TimelineController = AbstractController.extend({
|
|
||||||
custom_events: _.extend({}, AbstractController.prototype.custom_events, {
|
|
||||||
onGroupClick: "_onGroupClick",
|
|
||||||
onUpdate: "_onUpdate",
|
|
||||||
onRemove: "_onRemove",
|
|
||||||
onMove: "_onMove",
|
|
||||||
onAdd: "_onAdd",
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
init: function (parent, model, renderer, params) {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
this.open_popup_action = params.open_popup_action;
|
|
||||||
this.date_start = params.date_start;
|
|
||||||
this.date_stop = params.date_stop;
|
|
||||||
this.date_delay = params.date_delay;
|
|
||||||
this.context = params.actionContext;
|
|
||||||
this.moveQueue = [];
|
|
||||||
this.debouncedInternalMove = _.debounce(this.internalMove, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
update: function (params, options) {
|
|
||||||
const res = this._super.apply(this, arguments);
|
|
||||||
if (_.isEmpty(params)) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
const defaults = _.defaults({}, options, {
|
|
||||||
adjust_window: true,
|
|
||||||
});
|
|
||||||
const domains = params.domain || this.renderer.last_domains || [];
|
|
||||||
const contexts = params.context || [];
|
|
||||||
const group_bys = params.groupBy || this.renderer.last_group_bys || [];
|
|
||||||
this.last_domains = domains;
|
|
||||||
this.last_contexts = contexts;
|
|
||||||
// Select the group by
|
|
||||||
let n_group_bys = group_bys;
|
|
||||||
if (!n_group_bys.length && this.renderer.arch.attrs.default_group_by) {
|
|
||||||
n_group_bys = this.renderer.arch.attrs.default_group_by.split(",");
|
|
||||||
}
|
|
||||||
this.renderer.last_group_bys = n_group_bys;
|
|
||||||
this.renderer.last_domains = domains;
|
|
||||||
|
|
||||||
let fields = this.renderer.fieldNames;
|
|
||||||
fields = _.uniq(fields.concat(n_group_bys));
|
|
||||||
$.when(
|
|
||||||
res,
|
|
||||||
this._rpc({
|
|
||||||
model: this.model.modelName,
|
|
||||||
method: "search_read",
|
|
||||||
kwargs: {
|
|
||||||
fields: fields,
|
|
||||||
domain: domains,
|
|
||||||
order: [{name: this.renderer.arch.attrs.default_group_by}],
|
|
||||||
},
|
|
||||||
context: this.getSession().user_context,
|
|
||||||
}).then((data) =>
|
|
||||||
this.renderer.on_data_loaded(
|
|
||||||
data,
|
|
||||||
n_group_bys,
|
|
||||||
defaults.adjust_window
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets triggered when a group in the timeline is
|
|
||||||
* clicked (by the TimelineRenderer).
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {EventObject} event
|
|
||||||
* @returns {jQuery.Deferred}
|
|
||||||
*/
|
|
||||||
_onGroupClick: function (event) {
|
|
||||||
const groupField = this.renderer.last_group_bys[0];
|
|
||||||
return this.do_action({
|
|
||||||
type: "ir.actions.act_window",
|
|
||||||
res_model: this.renderer.fields[groupField].relation,
|
|
||||||
res_id: event.data.item.group,
|
|
||||||
target: "new",
|
|
||||||
views: [[false, "form"]],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a form view of a clicked timeline
|
|
||||||
* item (triggered by the TimelineRenderer).
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {EventObject} event
|
|
||||||
*/
|
|
||||||
_onUpdate: function (event) {
|
|
||||||
this.renderer = event.data.renderer;
|
|
||||||
const rights = event.data.rights;
|
|
||||||
const item = event.data.item;
|
|
||||||
const id = Number(item.evt.id) || item.evt.id;
|
|
||||||
const title = item.evt.__name;
|
|
||||||
if (this.open_popup_action) {
|
|
||||||
new dialogs.FormViewDialog(this, {
|
|
||||||
res_model: this.model.modelName,
|
|
||||||
res_id: id,
|
|
||||||
context: this.getSession().user_context,
|
|
||||||
title: title,
|
|
||||||
view_id: Number(this.open_popup_action),
|
|
||||||
on_saved: () => {
|
|
||||||
this.write_completed();
|
|
||||||
},
|
|
||||||
}).open();
|
|
||||||
} else {
|
|
||||||
let mode = "readonly";
|
|
||||||
if (rights.write) {
|
|
||||||
mode = "edit";
|
|
||||||
}
|
|
||||||
this.trigger_up("switch_view", {
|
|
||||||
view_type: "form",
|
|
||||||
res_id: id,
|
|
||||||
mode: mode,
|
|
||||||
model: this.model.modelName,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets triggered when a timeline item is
|
|
||||||
* moved (triggered by the TimelineRenderer).
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {EventObject} event
|
|
||||||
*/
|
|
||||||
_onMove: function (event) {
|
|
||||||
const item = event.data.item;
|
|
||||||
const fields = this.renderer.fields;
|
|
||||||
const event_start = item.start;
|
|
||||||
const event_end = item.end;
|
|
||||||
let group = false;
|
|
||||||
if (item.group !== -1) {
|
|
||||||
group = item.group;
|
|
||||||
}
|
|
||||||
const data = {};
|
|
||||||
// In case of a move event, the date_delay stay the same,
|
|
||||||
// only date_start and stop must be updated
|
|
||||||
data[this.date_start] = time.auto_date_to_str(
|
|
||||||
event_start,
|
|
||||||
fields[this.date_start].type
|
|
||||||
);
|
|
||||||
if (this.date_stop) {
|
|
||||||
// In case of instantaneous event, item.end is not defined
|
|
||||||
if (event_end) {
|
|
||||||
data[this.date_stop] = time.auto_date_to_str(
|
|
||||||
event_end,
|
|
||||||
fields[this.date_stop].type
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
data[this.date_stop] = data[this.date_start];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.date_delay && event_end) {
|
|
||||||
const diff_seconds = Math.round(
|
|
||||||
(event_end.getTime() - event_start.getTime()) / 1000
|
|
||||||
);
|
|
||||||
data[this.date_delay] = diff_seconds / 3600;
|
|
||||||
}
|
|
||||||
const grouped_field = this.renderer.last_group_bys[0];
|
|
||||||
this._rpc({
|
|
||||||
model: this.modelName,
|
|
||||||
method: "fields_get",
|
|
||||||
args: [grouped_field],
|
|
||||||
context: this.getSession().user_context,
|
|
||||||
}).then(async (fields_processed) => {
|
|
||||||
if (
|
|
||||||
this.renderer.last_group_bys &&
|
|
||||||
this.renderer.last_group_bys instanceof Array &&
|
|
||||||
fields_processed[grouped_field].type !== "many2many"
|
|
||||||
) {
|
|
||||||
data[this.renderer.last_group_bys[0]] = group;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.moveQueue.push({
|
|
||||||
id: event.data.item.id,
|
|
||||||
data: data,
|
|
||||||
event: event,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.debouncedInternalMove();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write enqueued moves to Odoo. After all writes are finished it updates
|
|
||||||
* the view once (prevents flickering of the view when multiple timeline items
|
|
||||||
* are moved at once).
|
|
||||||
*
|
|
||||||
* @returns {jQuery.Deferred}
|
|
||||||
*/
|
|
||||||
internalMove: function () {
|
|
||||||
const queues = this.moveQueue.slice();
|
|
||||||
this.moveQueue = [];
|
|
||||||
const defers = [];
|
|
||||||
for (const item of queues) {
|
|
||||||
defers.push(
|
|
||||||
this._rpc({
|
|
||||||
model: this.model.modelName,
|
|
||||||
method: "write",
|
|
||||||
args: [[item.event.data.item.id], item.data],
|
|
||||||
context: this.getSession().user_context,
|
|
||||||
}).then(() => {
|
|
||||||
item.event.data.callback(item.event.data.item);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return $.when.apply($, defers).done(() => {
|
|
||||||
this.write_completed({
|
|
||||||
adjust_window: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a timeline item gets removed from the view.
|
|
||||||
* Requires user confirmation before it gets actually deleted.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {EventObject} event
|
|
||||||
* @returns {jQuery.Deferred}
|
|
||||||
*/
|
|
||||||
_onRemove: function (event) {
|
|
||||||
var def = $.Deferred();
|
|
||||||
|
|
||||||
Dialog.confirm(this, _t("Are you sure you want to delete this record?"), {
|
|
||||||
title: _t("Warning"),
|
|
||||||
confirm_callback: () => {
|
|
||||||
this.remove_completed(event).then(def.resolve.bind(def));
|
|
||||||
},
|
|
||||||
cancel_callback: def.resolve.bind(def),
|
|
||||||
});
|
|
||||||
|
|
||||||
return def;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a timeline item gets added and opens a form view.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {EventObject} event
|
|
||||||
* @returns {dialogs.FormViewDialog}
|
|
||||||
*/
|
|
||||||
_onAdd: function (event) {
|
|
||||||
const item = event.data.item;
|
|
||||||
// Initialize default values for creation
|
|
||||||
const default_context = {};
|
|
||||||
default_context["default_".concat(this.date_start)] = item.start;
|
|
||||||
if (this.date_delay) {
|
|
||||||
default_context["default_".concat(this.date_delay)] = 1;
|
|
||||||
}
|
|
||||||
if (this.date_start) {
|
|
||||||
default_context["default_".concat(this.date_start)] = moment(item.start)
|
|
||||||
.utc()
|
|
||||||
.format("YYYY-MM-DD HH:mm:ss");
|
|
||||||
}
|
|
||||||
if (this.date_stop && item.end) {
|
|
||||||
default_context["default_".concat(this.date_stop)] = moment(item.end)
|
|
||||||
.utc()
|
|
||||||
.format("YYYY-MM-DD HH:mm:ss");
|
|
||||||
}
|
|
||||||
if (item.group > 0) {
|
|
||||||
default_context["default_".concat(this.renderer.last_group_bys[0])] =
|
|
||||||
item.group;
|
|
||||||
}
|
|
||||||
// Show popup
|
|
||||||
new dialogs.FormViewDialog(this, {
|
|
||||||
res_model: this.model.modelName,
|
|
||||||
res_id: null,
|
|
||||||
context: _.extend(default_context, this.context),
|
|
||||||
view_id: Number(this.open_popup_action),
|
|
||||||
on_saved: (record) => {
|
|
||||||
this.create_completed([record.res_id]);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.open()
|
|
||||||
.on("closed", this, () => {
|
|
||||||
event.data.callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered upon completion of a new record.
|
|
||||||
* Updates the timeline view with the new record.
|
|
||||||
*
|
|
||||||
* @param {RecordId} id
|
|
||||||
* @returns {jQuery.Deferred}
|
|
||||||
*/
|
|
||||||
create_completed: function (id) {
|
|
||||||
return this._rpc({
|
|
||||||
model: this.model.modelName,
|
|
||||||
method: "read",
|
|
||||||
args: [id, this.model.fieldNames],
|
|
||||||
context: this.context,
|
|
||||||
}).then((records) => {
|
|
||||||
var new_event = this.renderer.event_data_transform(records[0]);
|
|
||||||
var items = this.renderer.timeline.itemsData;
|
|
||||||
items.add(new_event);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered upon completion of writing a record.
|
|
||||||
* @param {ControllerOptions} options
|
|
||||||
*/
|
|
||||||
write_completed: function (options) {
|
|
||||||
const params = {
|
|
||||||
domain: this.renderer.last_domains,
|
|
||||||
context: this.context,
|
|
||||||
groupBy: this.renderer.last_group_bys,
|
|
||||||
};
|
|
||||||
this.update(params, options);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered upon confirm of removing a record.
|
|
||||||
* @param {EventObject} event
|
|
||||||
* @returns {jQuery.Deferred}
|
|
||||||
*/
|
|
||||||
remove_completed: function (event) {
|
|
||||||
return this._rpc({
|
|
||||||
model: this.modelName,
|
|
||||||
method: "unlink",
|
|
||||||
args: [[event.data.item.id]],
|
|
||||||
context: this.getSession().user_context,
|
|
||||||
}).then(() => {
|
|
||||||
let unlink_index = false;
|
|
||||||
for (var i = 0; i < this.model.data.data.length; i++) {
|
|
||||||
if (this.model.data.data[i].id === event.data.item.id) {
|
|
||||||
unlink_index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isNaN(unlink_index)) {
|
|
||||||
this.model.data.data.splice(unlink_index, 1);
|
|
||||||
}
|
|
||||||
event.data.callback(event.data.item);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return TimelineController;
|
|
||||||
});
|
|
|
@ -2,6 +2,7 @@
|
||||||
/* Odoo web_timeline
|
/* Odoo web_timeline
|
||||||
* Copyright 2015 ACSONE SA/NV
|
* Copyright 2015 ACSONE SA/NV
|
||||||
* Copyright 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
* Copyright 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||||
|
* Copyright 2023 Onestein - Anjeel Haria
|
||||||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
||||||
|
|
||||||
odoo.define("web_timeline.TimelineView", function (require) {
|
odoo.define("web_timeline.TimelineView", function (require) {
|
||||||
|
@ -23,7 +24,7 @@ odoo.define("web_timeline.TimelineView", function (require) {
|
||||||
|
|
||||||
var TimelineView = AbstractView.extend({
|
var TimelineView = AbstractView.extend({
|
||||||
display_name: _lt("Timeline"),
|
display_name: _lt("Timeline"),
|
||||||
icon: "fa-tasks",
|
icon: "fa fa-tasks",
|
||||||
jsLibs: ["/web_timeline/static/lib/vis-timeline/vis-timeline-graph2d.js"],
|
jsLibs: ["/web_timeline/static/lib/vis-timeline/vis-timeline-graph2d.js"],
|
||||||
cssLibs: ["/web_timeline/static/lib/vis-timeline/vis-timeline-graph2d.css"],
|
cssLibs: ["/web_timeline/static/lib/vis-timeline/vis-timeline-graph2d.css"],
|
||||||
config: _.extend({}, AbstractView.prototype.config, {
|
config: _.extend({}, AbstractView.prototype.config, {
|
||||||
|
|
Loading…
Reference in New Issue