mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-26 07:50:05 -05:00
[ADD] Web Gantt Chart
This commit is contained in:
1 parent
0088cf48d6
commit
7d98387f0d
17 files changed
+1029
No files matched your search
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
'name': 'Project Gantt Chart Demo',
|
||||
'category': 'Project',
|
||||
'version': '2.0',
|
||||
'summary': 'Project Gantt Chart Demo',
|
||||
'depends': [
|
||||
'web_gantt',
|
||||
'project'
|
||||
],
|
||||
'data': [
|
||||
'views/project_task_inherit.xml',
|
||||
],
|
||||
'demo': ['data/project_task_demo.xml'],
|
||||
'qweb': [],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'application': False,
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<flectra>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="project.project_task_1" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">9</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_2" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today())"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">7</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_3" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=2))"/>
|
||||
<field name="end_date" eval="(DateTime.today())"/>
|
||||
<field name="progress">5</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_4" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=2))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=2))"/>
|
||||
<field name="progress">5</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_5" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today())"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=2))"/>
|
||||
<field name="progress">4</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_6" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=3))"/>
|
||||
<field name="progress">9</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_7" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=2))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">8</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_8" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=2))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=3))"/>
|
||||
<field name="progress">10</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_9" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today())"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">0</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_10" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">7</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_11" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=3))"/>
|
||||
<field name="progress">1</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_12" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">2</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_19" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=2))"/>
|
||||
<field name="progress">3</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_20" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today())"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=3))"/>
|
||||
<field name="progress">3</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_21" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=2))"/>
|
||||
<field name="end_date" eval="(DateTime.today())"/>
|
||||
<field name="progress">4</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_22" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=2))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">7</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_24" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=1))"/>
|
||||
<field name="progress">4</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_25" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today() + relativedelta(days=2))"/>
|
||||
<field name="progress">2</field>
|
||||
</record>
|
||||
|
||||
<record id="project.project_task_26" model="project.task">
|
||||
<field name="start_date" eval="(DateTime.today() - relativedelta(days=1))"/>
|
||||
<field name="end_date" eval="(DateTime.today())"/>
|
||||
<field name="progress">10</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</flectra>
|
||||
@@ -0,0 +1 @@
|
||||
from . import project_task
|
||||
@@ -0,0 +1,9 @@
|
||||
from flectra import fields, models
|
||||
|
||||
|
||||
class ProjectTaskInherit(models.Model):
|
||||
_inherit = 'project.task'
|
||||
|
||||
start_date = fields.Date(string='Start Date')
|
||||
end_date = fields.Date(string='End Date')
|
||||
progress = fields.Integer(string='Progress')
|
||||
@@ -0,0 +1,66 @@
|
||||
<flectra>
|
||||
|
||||
<record id="project_task_view_gantt_inherit" model="ir.ui.view">
|
||||
<field name="name">project.task.form.view</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_form2"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="start_date"/>
|
||||
<field name="end_date"/>
|
||||
<field name="progress"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_project_task_gantt" model="ir.ui.view">
|
||||
<field name="name">project.task.gantt</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<gantt string="Project Tasks" mode="day">
|
||||
<field name="progress" type="progress" scale="10"/>
|
||||
<field name="start_date" type="start"/>
|
||||
<field name="end_date" type="end"/>
|
||||
<!-- <field name="child_task_ids" type="child" start="sd" end="ed"/>-->
|
||||
</gantt>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_all_task_tree_action_inherit" model="ir.actions.act_window.view">
|
||||
<field eval="1" name="sequence"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="project.view_task_tree2"/>
|
||||
<field name="act_window_id" ref="project.action_view_all_task"/>
|
||||
</record>
|
||||
|
||||
<record id="action_view_all_task_kanban_action_inherit" model="ir.actions.act_window.view">
|
||||
<field eval="2" name="sequence"/>
|
||||
<field name="view_mode">kanban</field>
|
||||
<field name="view_id" ref="project.view_task_kanban"/>
|
||||
<field name="act_window_id" ref="project.action_view_all_task"/>
|
||||
</record>
|
||||
|
||||
<record id="action_view_all_task_form_action_inherit" model="ir.actions.act_window.view">
|
||||
<field eval="3" name="sequence"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="project.view_task_form2"/>
|
||||
<field name="act_window_id" ref="project.action_view_all_task"/>
|
||||
</record>
|
||||
|
||||
<record id="action_view_all_task_calendar_action_inherit" model="ir.actions.act_window.view">
|
||||
<field eval="4" name="sequence"/>
|
||||
<field name="view_mode">calendar</field>
|
||||
<field name="view_id" ref="project.view_task_calendar"/>
|
||||
<field name="act_window_id" ref="project.action_view_all_task"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="action_view_all_task_gantt_chart_inherit" model="ir.actions.act_window.view">
|
||||
<field eval="5" name="sequence"/>
|
||||
<field name="view_mode">gantt</field>
|
||||
<field name="view_id" ref="project_gantt.view_project_task_gantt"/>
|
||||
<field name="act_window_id" ref="project.action_view_all_task"/>
|
||||
</record>
|
||||
|
||||
|
||||
</flectra>
|
||||
Whitespace-only changes.
@@ -0,0 +1,18 @@
|
||||
{
|
||||
'name': 'Web Gantt Chart',
|
||||
'category': 'Project',
|
||||
'version': '2.0',
|
||||
'summary': 'Gantt Chart',
|
||||
'depends': [
|
||||
'web'
|
||||
],
|
||||
'data': [
|
||||
'views/assets.xml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/*.xml'
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'application': False,
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.o_gantt_renderer{
|
||||
height: 100%;
|
||||
}
|
||||
.gantt_grid_head_add, .gantt_add{
|
||||
display: none;
|
||||
}
|
||||
.gantt_task_progress_drag{
|
||||
display: none !important;
|
||||
}
|
||||
.gantt_scale_line > .gantt_scale_cell, .gantt_grid_scale > .gantt_grid_head_cell{
|
||||
font-weight: bold !important;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.gridHoverStyle,.gridSelection,.timelineSelection{background-color:#fff3a1}.gantt_grid_scale .gantt_grid_head_cell{color:#a6a6a6;border-top:none!important;border-right:none!important}.gantt_grid_data .gantt_cell{border-right:none;color:#454545}.gantt_task_link .gantt_link_arrow_right{border-width:6px;margin-top:-3px}.gantt_task_link .gantt_link_arrow_left{border-width:6px;margin-left:-6px;margin-top:-3px}.gantt_task_link .gantt_link_arrow_down,.gantt_task_link .gantt_link_arrow_up{border-width:6px}.gantt_task_line .gantt_task_progress_drag{bottom:-4px;height:10px;margin-left:-8px;width:16px}.chartHeaderBg{background-color:#fff}.gantt_task .gantt_task_scale .gantt_scale_cell{color:#a6a6a6;border-right:1px solid #ebebeb}.gantt_row.gantt_project,.gantt_row.odd.gantt_project{background-color:#edffef}.gantt_task_row.gantt_project,.gantt_task_row.odd.gantt_project{background-color:#f5fff6}.gantt_task_line.gantt_project{background-color:#65c16f;border:1px solid #3c9445}.gantt_task_line.gantt_project .gantt_task_progress{background-color:#46ad51}.buttonBg{background:#fff}.gantt_cal_light .gantt_btn_set{margin:5px 10px}.gantt_btn_set.gantt_cancel_btn_set{background:#fff;color:#454545;border:1px solid #cecece}.gantt_btn_set.gantt_save_btn_set{background:#3db9d3;text-shadow:0 -1px 0 #248a9f;color:#fff}.gantt_btn_set.gantt_delete_btn_set{text-shadow:0 -1px 0 #6f6f6f;background:#ec8e00;text-shadow:0 -1px 0 #a60;color:#fff}.gantt_cal_light_wide{padding-left:0!important;padding-right:0!important}.gantt_cal_light_wide .gantt_cal_larea{border-left:none!important;border-right:none!important}.gantt_popup_button.gantt_ok_button{background:#3db9d3;text-shadow:0 -1px 0 #248a9f;color:#fff;font-weight:700;border-width:0}.gantt_popup_button.gantt_cancel_button{font-weight:700;color:#454544}.gantt_popup_title{background-color:#fff}.gantt_popup_shadow{box-shadow:3px 3px 3px rgba(0,0,0,.07)}.gantt_qi_big_icon.icon_edit{color:#454545;background:#fff}.gantt_qi_big_icon.icon_delete{text-shadow:0 -1px 0 #a60;background:#ec8e00;color:#fff;border-width:0}.gantt_tooltip{box-shadow:3px 3px 3px rgba(0,0,0,.07);border-left:1px solid rgba(0,0,0,.07);border-top:1px solid rgba(0,0,0,.07);font-size:8pt;color:#454545}.gantt_container,.gantt_tooltip{background-color:#fff;font-family:Arial}.gantt_container{font-size:13px;border:1px solid #cecece;position:relative;white-space:nowrap;overflow-x:hidden;overflow-y:hidden}.gantt_touch_active{overscroll-behavior:none}.gantt_task_scroll{overflow-x:scroll}.gantt_grid,.gantt_task{position:relative;overflow-x:hidden;overflow-y:hidden;display:inline-block;vertical-align:top}.gantt_grid_scale,.gantt_task_scale{color:#6b6b6b;font-size:12px;border-bottom:1px solid #cecece;box-sizing:border-box}.gantt_grid_scale,.gantt_task_scale,.gantt_task_vscroll{background-color:#fff}.gantt_scale_line{box-sizing:border-box;-moz-box-sizing:border-box;border-top:1px solid #cecece}.gantt_scale_line:first-child{border-top:none}.gantt_grid_head_cell{display:inline-block;vertical-align:top;border-right:1px solid #cecece;text-align:center;position:relative;cursor:default;height:100%;box-sizing:border-box;-moz-box-sizing:border-box;line-height:33px;-moz-user-select:-moz-none;-webkit-user-select:none;user-select:none;overflow:hidden}.gantt_scale_line{clear:both}.gantt_grid_data{width:100%;overflow:hidden;position:relative}.gantt_row{position:relative;-webkit-user-select:none;-moz-user-select:none;-moz-user-select:-moz-none}.gantt_add,.gantt_grid_head_add{width:100%;height:100%;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTQ3MjMyMENDNkI0MTFFMjk4MTI5QTg3MDhFNDVDQTkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTQ3MjMyMERDNkI0MTFFMjk4MTI5QTg3MDhFNDVDQTkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1NDcyMzIwQUM2QjQxMUUyOTgxMjlBODcwOEU0NUNBOSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1NDcyMzIwQkM2QjQxMUUyOTgxMjlBODcwOEU0NUNBOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PshZT8UAAABbSURBVHjaYrTdeZmBEsCER+4wEP+H4sPkGGCDg020ARR7gb4GIAcYDKMDdPnDyAbYkGG5DVW9cIQMvUdBBAuUY4vDz8iAcZinA2zgCHqAYQMseAywJcYFAAEGAM+UFGuohFczAAAAAElFTkSuQmCC);background-position:50%;background-repeat:no-repeat;cursor:pointer;position:relative;-moz-opacity:.3;opacity:.3Line truncated
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
@license
|
||||
|
||||
dhtmlxGantt v.7.0.11 Standard
|
||||
|
||||
This version of dhtmlxGantt is distributed under GPL 2.0 license and can be legally used in GPL projects.
|
||||
|
||||
To use dhtmlxGantt in non-GPL projects (and get Pro version of the product), please obtain Commercial/Enterprise or Ultimate license on our site https://dhtmlx.com/docs/products/dhtmlxGantt/#licensing or contact us at sales@dhtmlx.com
|
||||
|
||||
(c) XB Software Ltd.
|
||||
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("dhtmlxgantt",[],e):"object"==typeof exports?exports.dhtmlxgantt=e():t.dhtmlxgantt=e()}(window,function(){return function(t){var e={};function n(i){if(e[i])return e[i].exports;var r=e[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(i,r,function(e){return t[e]}.bind(null,r));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/codebase/",n(n.s=245)}([function(t,e,n){var i,r=n(3);t.exports={copy:function t(e){var n,i;if(e&&"object"==typeof e)switch(!0){case r.isDate(e):i=new Date(e);break;case r.isArray(e):for(i=new Array(e.length),n=0;n<e.length;n++)i[n]=t(e[n]);break;case r.isStringObject(e):i=new String(e);break;case r.isNumberObject(e):i=new Number(e);break;case r.isBooleanObject(e):i=new Boolean(e);break;default:for(n in i=function(t){return t.constructor.toString()!=={}.constructor.toString()}(e)?Object.create(e):{},e)Object.prototype.hasOwnProperty.apply(e,[n])&&(i[n]=t(e[n]))}return i||e},defined:function(t){return void 0!==t},mixin:function(t,e,n){for(var i in e)(void 0===t[i]||n)&&(t[i]=e[i]);return t},uid:function(){return i||(i=(new Date).valueOf()),++i},bind:function(t,e){return t.bind?t.bind(e):function(){return t.apply(e,arguments)}},event:function(t,e,n,i){t.addEventListener?t.addEventListener(e,n,void 0!==i&&i):t.attachEvent&&t.attachEvent("on"+e,n)},eventRemove:function(t,e,n,i){t.removeEventListener?t.removeEventListener(e,n,void 0!==i&&i):t.detachEvent&&t.detachEvent("on"+e,n)}}},function(t,e){function n(t){var e=0,n=0,i=0,r=0;if(t.getBoundingClientRect){var a=t.getBoundingClientRect(),o=document.body,s=document.documentElement||document.body.parentNode||document.body,l=window.pageYOffset||s.scrollTop||o.scrollTop,c=window.pageXOffset||s.scrollLeft||o.scrollLeft,u=s.clientTop||o.clientTop||0,d=s.clientLeft||o.clientLeft||0;e=a.top+l-u,n=a.left+c-d,i=document.body.offsetWidth-a.right,r=document.body.offsetHeight-a.bottom}else{for(;t;)e+=parseInt(t.offsetTop,10),n+=parseInt(t.offsetLeft,10),t=t.offsetParent;i=document.body.offsetWidth-t.offsetWidth-n,r=document.body.offsetHeight-t.offsetHeight-e}return{y:Math.round(e),x:Math.round(n),width:t.offsetWidth,height:t.offsetHeight,right:Math.round(i),bottom:Math.round(r)}}function i(t){var e=!1,n=!1;if(window.getComputedStyle){var i=window.getComputedStyle(t,null);e=i.display,n=i.visibility}else t.currentStyle&&(e=t.currentStyle.display,n=t.currentStyle.visibility);return"none"!=e&&"hidden"!=n}function r(t){return!isNaN(t.getAttribute("tabindex"))&&1*t.getAttribute("tabindex")>=0}function a(t){return!{a:!0,area:!0}[t.nodeName.loLowerCase()]||!!t.getAttribute("href")}function o(t){return!{input:!0,select:!0,textarea:!0,button:!0,object:!0}[t.nodeName.toLowerCase()]||!t.hasAttribute("disabled")}function s(t){if(!t)return"";var e=t.className||"";return e.baseVal&&(e=e.baseVal),e.indexOf||(e=""),u(e)}var l;function c(t){return t.tagName?t:(t=t||window.event).target||t.srcElement}function u(t){return(String.prototype.trim||function(){return this.replace(/^\s+|\s+$/g,"")}).apply(t)}t.exports={getNodePosition:n,getFocusableNodes:function(t){for(var e=t.querySelectorAll(["a[href]","area[href]","input","select","textarea","button","iframe","object","embed","[tabindex]","[contenteditable]"].join(", ")),n=Array.prototype.slice.call(e,0),s=0;s<n.length;s++){var l=n[s];(r(l)||o(l)||a(l))&&i(l)||(n.splice(s,1),s--)}return n},getScrollSize:function(){var t=document.createElement("div");t.style.cssText="visibility:hidden;position:absolute;left:-1000px;width:100px;padding:0px;margin:0px;height:110px;min-height:100px;overflow-y:scroll;",document.body.appendChild(t);var e=t.offsetWidth-t.clientWidth;return document.body.removeChild(t),e},getClassName:s,addClassName:function(t,e){e&&-1===t.className.indexOf(e)&&(t.className+=" "+e)},removeClassName:function(t,e){e=e.split(" ");for(var n=0;n<e.length;n++){var i=new RegExp("\\s?\\b"+e[n]+"\\b(?![-_.])","");t.className=t.className.replace(i,"")}},insertNode:function(t,e){l||(l=document.createElement("div")),l.innerHTML=e;var n=l.firstChild;return t.appendChild(n),n},removeNode:function(t){t&&t.parentNode&&t.parentNode.removeChild(t)},getChildNodes:function(t,e){for(var n=t.childNodes,i=n.length,r=[],a=0;a<i;a++){vaLine truncated
|
||||
//# sourceMappingURL=dhtmlxgantt.js.map
|
||||
@@ -0,0 +1,147 @@
|
||||
flectra.define('web_gantt.GanttController', function (require) {
|
||||
"use strict";
|
||||
|
||||
|
||||
const AbstractController = require('web.AbstractController');
|
||||
const { ComponentWrapper } = require('web.OwlCompatibility');
|
||||
const DropdownMenu = require('web.DropdownMenu');
|
||||
const { DEFAULT_INTERVAL, INTERVAL_OPTIONS } = require('web.searchUtils');
|
||||
const { qweb } = require('web.core');
|
||||
var view_dialogs = require('web.view_dialogs');
|
||||
|
||||
class CarretDropdownMenu extends DropdownMenu {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get displayCaret() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var GanttChartController = AbstractController.extend({
|
||||
events: _.extend({}, AbstractController.prototype.events, {
|
||||
'click .gantt_bar_task': '_onTaskClicked',
|
||||
}),
|
||||
custom_events: _.extend({}, AbstractController.prototype.custom_events, {
|
||||
item_selected: '_onItemSelected',
|
||||
open_view: '_onOpenView',
|
||||
}),
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {Widget} parent
|
||||
* @param {GraphModel} model
|
||||
* @param {GraphRenderer} renderer
|
||||
* @param {Object} params
|
||||
* @param {string[]} params.measures
|
||||
* @param {boolean} params.isEmbedded
|
||||
* @param {string[]} params.groupableFields,
|
||||
*/
|
||||
init: function (parent, model, renderer, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.measures = params.measures;
|
||||
this.child = params.child;
|
||||
this.childExist = params.childExist;
|
||||
// this parameter condition the appearance of a 'Group By'
|
||||
// button in the control panel owned by the graph view.
|
||||
this.isEmbedded = params.isEmbedded;
|
||||
this.withButtons = params.withButtons;
|
||||
// views to use in the action triggered when the graph is clicked
|
||||
this.views = params.views;
|
||||
this.title = params.title;
|
||||
|
||||
// this parameter determines what is the list of fields
|
||||
// that may be used within the groupby menu available when
|
||||
// the view is embedded
|
||||
this.groupableFields = params.groupableFields;
|
||||
this.buttonDropdownPromises = [];
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
this.$el.addClass('o_gantt_controller');
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
getOwnedQueryParams: function () {
|
||||
var state = this.model.get();
|
||||
return {
|
||||
context: {
|
||||
gantt_mode: state.mode,
|
||||
}
|
||||
};
|
||||
},
|
||||
destroy: function () {
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
_onTaskClicked:function(ev){
|
||||
var $target = $(ev.currentTarget);
|
||||
if($target.attr('task_id').startsWith('child_')){
|
||||
let id = $target.attr('task_id').substring("child_".length);
|
||||
|
||||
return new view_dialogs.FormViewDialog(this, {
|
||||
title: $target.find('.gantt_task_content').text(),
|
||||
res_model: this.child.model,
|
||||
view_id: false,
|
||||
res_id: parseInt(id),
|
||||
on_saved: this.reload.bind(this, {}),
|
||||
}).open();
|
||||
|
||||
}else{
|
||||
return new view_dialogs.FormViewDialog(this, {
|
||||
title: $target.find('.gantt_task_content').text(),
|
||||
res_model: this.modelName,
|
||||
view_id: false,
|
||||
res_id: parseInt($target.attr('task_id')),
|
||||
on_saved: this.reload.bind(this, {}),
|
||||
}).open();
|
||||
}
|
||||
},
|
||||
_onButtonClick:function(ev){
|
||||
var $target = $(ev.target);
|
||||
if($target.hasClass('o_gantt_button')){
|
||||
this.update({ mode: $target.data('mode') });
|
||||
}else{
|
||||
return new view_dialogs.FormViewDialog(this, {
|
||||
res_model: this.modelName,
|
||||
view_id: false,
|
||||
res_id: false,
|
||||
on_saved: this.reload.bind(this, {}),
|
||||
}).open();
|
||||
}
|
||||
},
|
||||
updateButtons: function () {
|
||||
if (!this.$buttons) {
|
||||
return;
|
||||
}
|
||||
var state = this.model.get();
|
||||
this.$buttons.find('.o_gantt_button').removeClass('active');
|
||||
this.$buttons
|
||||
.find('.o_gantt_button[data-mode="' + state.mode + '"]')
|
||||
.addClass('active');
|
||||
},
|
||||
formatDate: function(date){
|
||||
var d = new Date(date),
|
||||
month = '' + (d.getMonth() + 1),
|
||||
day = '' + d.getDate(),
|
||||
year = d.getFullYear();
|
||||
|
||||
if (month.length < 2)
|
||||
month = '0' + month;
|
||||
if (day.length < 2)
|
||||
day = '0' + day;
|
||||
|
||||
return [day, month, year].join('-');
|
||||
},
|
||||
reload: async function () {
|
||||
const promises = [this._super(...arguments)];
|
||||
return Promise.all(promises);
|
||||
},
|
||||
renderButtons: function ($node) {
|
||||
this.$buttons = $(qweb.render('GanttView.buttons'));
|
||||
this.$buttons.click(ev => this._onButtonClick(ev));
|
||||
},
|
||||
|
||||
});
|
||||
return GanttChartController;
|
||||
});
|
||||
@@ -0,0 +1,232 @@
|
||||
flectra.define('web_gantt.GanttModel', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
const { DEFAULT_INTERVAL, rankInterval } = require('web.searchUtils');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
var AbstractModel = require('web.AbstractModel');
|
||||
|
||||
return AbstractModel.extend({
|
||||
/**
|
||||
* @override
|
||||
* @param {Widget} parent
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.chart = null;
|
||||
this.GanttData = [];
|
||||
},
|
||||
|
||||
__get: function () {
|
||||
return Object.assign({ isSample: this.isSampleModel }, this.chart);
|
||||
},
|
||||
__load: function (params) {
|
||||
var groupBys = params.context.graph_groupbys || params.groupBys;
|
||||
this.initialGroupBys = groupBys;
|
||||
this.fields = params.fields;
|
||||
this.modelName = params.modelName;
|
||||
this.chart = Object.assign({
|
||||
context: params.context,
|
||||
dataPoints: [],
|
||||
domain: params.domain,
|
||||
groupBy: params.groupedBy.length ? params.groupedBy : groupBys,
|
||||
measure: params.context.graph_measure || params.measure,
|
||||
mode: params.context.graph_mode || params.mode,
|
||||
origins: [],
|
||||
stacked: params.stacked,
|
||||
timeRanges: params.timeRanges,
|
||||
orderBy: params.orderBy,
|
||||
startDate: params.startDate,
|
||||
endDate: params.endDate,
|
||||
child: params.child,
|
||||
childExist: params.childExist,
|
||||
progress: params.progress,
|
||||
titleField: params.titleField,
|
||||
});
|
||||
|
||||
this._computeDerivedParams();
|
||||
|
||||
return this._loadGraph();
|
||||
},
|
||||
__reload: function (handle, params) {
|
||||
if ('context' in params) {
|
||||
this.chart.context = params.context;
|
||||
this.chart.groupBy = params.context.graph_groupbys || this.chart.groupBy;
|
||||
this.chart.measure = params.context.graph_measure || this.chart.measure;
|
||||
this.chart.mode = params.context.graph_mode || this.chart.mode;
|
||||
}
|
||||
if ('domain' in params) {
|
||||
this.chart.domain = params.domain;
|
||||
}
|
||||
if ('groupBy' in params) {
|
||||
this.chart.groupBy = params.groupBy.length ? params.groupBy : this.initialGroupBys;
|
||||
}
|
||||
if ('measure' in params) {
|
||||
this.chart.measure = params.measure;
|
||||
}
|
||||
if ('timeRanges' in params) {
|
||||
this.chart.timeRanges = params.timeRanges;
|
||||
}
|
||||
|
||||
this._computeDerivedParams();
|
||||
|
||||
if ('mode' in params) {
|
||||
this.chart.mode = params.mode;
|
||||
return Promise.resolve();
|
||||
}
|
||||
if ('stacked' in params) {
|
||||
this.chart.stacked = params.stacked;
|
||||
return Promise.resolve();
|
||||
}
|
||||
if ('orderBy' in params) {
|
||||
this.chart.orderBy = params.orderBy;
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._loadGraph();
|
||||
},
|
||||
_computeDerivedParams: function () {
|
||||
this.chart.processedGroupBy = this._processGroupBy(this.chart.groupBy);
|
||||
|
||||
const { range, rangeDescription, comparisonRange, comparisonRangeDescription, fieldName } = this.chart.timeRanges;
|
||||
if (range) {
|
||||
this.chart.domains = [
|
||||
this.chart.domain.concat(range),
|
||||
this.chart.domain.concat(comparisonRange),
|
||||
];
|
||||
this.chart.origins = [rangeDescription, comparisonRangeDescription];
|
||||
const groupBys = this.chart.processedGroupBy.map(function (gb) {
|
||||
return gb.split(":")[0];
|
||||
});
|
||||
this.chart.comparisonFieldIndex = groupBys.indexOf(fieldName);
|
||||
} else {
|
||||
this.chart.domains = [this.chart.domain];
|
||||
this.chart.origins = [""];
|
||||
this.chart.comparisonFieldIndex = -1;
|
||||
}
|
||||
},
|
||||
_loadGraph: function () {
|
||||
var self = this;
|
||||
this.chart.dataPoints = [];
|
||||
this.chart.GanttData = [];
|
||||
var groupBy = this.chart.processedGroupBy;
|
||||
var fields = _.map(groupBy, function (groupBy) {
|
||||
return groupBy.split(':')[0];
|
||||
});
|
||||
if (this.chart.measure !== '__count__') {
|
||||
if (this.fields[this.chart.measure].type === 'many2one') {
|
||||
fields = fields.concat(this.chart.measure + ":count_distinct");
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
fields = fields.concat(this.chart.startDate);
|
||||
fields = fields.concat(this.chart.endDate);
|
||||
fields = fields.concat(this.chart.titleField);
|
||||
fields = fields.concat(this.chart.progress);
|
||||
if(this.chart.childExist){
|
||||
fields = fields.concat(this.chart.child.name);
|
||||
}
|
||||
var context = _.extend({fill_temporal: true}, this.chart.context);
|
||||
|
||||
var proms = [];
|
||||
proms.push(this._rpc({
|
||||
route: '/web/dataset/search_read',
|
||||
model: this.modelName,
|
||||
fields: fields,
|
||||
context: context,
|
||||
domain: this.chart.domain,
|
||||
}).then(self._processGanttData.bind(self)));
|
||||
return Promise.all(proms);
|
||||
},
|
||||
_processGanttData: async function(rawData) {
|
||||
var self = this;
|
||||
var childFields = ['display_name'];
|
||||
var proms = [];
|
||||
if(this.chart.childExist){
|
||||
childFields = childFields.concat(this.chart.child.startDate);
|
||||
childFields = childFields.concat(this.chart.child.endDate);
|
||||
}
|
||||
rawData.records.forEach(function (dataPt){
|
||||
if(dataPt[self.chart.startDate] != false && dataPt[self.chart.endDate] != false){
|
||||
var start_date = self.formatDate(dataPt[self.chart.startDate]);
|
||||
var end_date = self.formatDate(dataPt[self.chart.endDate]);
|
||||
var type = '';
|
||||
if(self.chart.childExist){
|
||||
type = dataPt[self.chart.child.name].length != 0 ? "project" : ''
|
||||
}
|
||||
self.chart.GanttData.push({
|
||||
resId: dataPt.id,
|
||||
startDate: start_date,
|
||||
type: type,
|
||||
endDate: end_date,
|
||||
titleField: dataPt[self.chart.titleField],
|
||||
progress: dataPt[self.chart.progress],
|
||||
});
|
||||
}
|
||||
if(self.chart.childExist && dataPt[self.chart.child.name].length != 0){
|
||||
proms.push(self._rpc({
|
||||
route: '/web/dataset/search_read',
|
||||
model: self.chart.child.model,
|
||||
fields: childFields,
|
||||
domain: [['id', 'in', dataPt[self.chart.child.name]]],
|
||||
}).then(self._processGanttChildData.bind(self,dataPt)));
|
||||
}
|
||||
});
|
||||
return Promise.all(proms);
|
||||
},
|
||||
_processGanttChildData: async function(data,rawData){
|
||||
var self = this;
|
||||
rawData.records.forEach(function (dataPt){
|
||||
var start_date = self.formatDate(dataPt[self.chart.child.startDate]);
|
||||
var end_date = self.formatDate(dataPt[self.chart.child.endDate]);
|
||||
self.chart.GanttData.push({
|
||||
resId: 'child_' + dataPt.id,
|
||||
startDate: start_date,
|
||||
endDate: end_date,
|
||||
titleField: dataPt['display_name'],
|
||||
parent: data.id,
|
||||
});
|
||||
});
|
||||
},
|
||||
formatDate: function(date){
|
||||
var d = new Date(date),
|
||||
month = '' + (d.getMonth() + 1),
|
||||
day = '' + d.getDate(),
|
||||
year = d.getFullYear();
|
||||
|
||||
if (month.length < 2)
|
||||
month = '0' + month;
|
||||
if (day.length < 2)
|
||||
day = '0' + day;
|
||||
|
||||
return [day, month, year].join('-');
|
||||
},
|
||||
_processGroupBy: function(groupBy) {
|
||||
const groupBysMap = new Map();
|
||||
for (const gb of groupBy) {
|
||||
let [fieldName, interval] = gb.split(':');
|
||||
const field = this.fields[fieldName];
|
||||
if (['date', 'datetime'].includes(field.type)) {
|
||||
interval = interval || DEFAULT_INTERVAL;
|
||||
}
|
||||
if (groupBysMap.has(fieldName)) {
|
||||
const registeredInterval = groupBysMap.get(fieldName);
|
||||
if (rankInterval(registeredInterval) < rankInterval(interval)) {
|
||||
groupBysMap.set(fieldName, interval);
|
||||
}
|
||||
} else {
|
||||
groupBysMap.set(fieldName, interval);
|
||||
}
|
||||
}
|
||||
return [...groupBysMap].map(([fieldName, interval]) => {
|
||||
if (interval) {
|
||||
return `${fieldName}:${interval}`;
|
||||
}
|
||||
return fieldName;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,167 @@
|
||||
flectra.define('web_gantt.GanttRenderer', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractRenderer = require('web.AbstractRenderer');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var dataComparisonUtils = require('web.dataComparisonUtils');
|
||||
var fieldUtils = require('web.field_utils');
|
||||
|
||||
var _t = core._t;
|
||||
var DateClasses = dataComparisonUtils.DateClasses;
|
||||
var qweb = core.qweb;
|
||||
|
||||
return AbstractRenderer.extend({
|
||||
className: "o_gantt_renderer",
|
||||
id: "o_gantt_renderer",
|
||||
sampleDataTargets: ['.o_graph_canvas_container'],
|
||||
/**
|
||||
* @override
|
||||
* @param {Widget} parent
|
||||
* @param {Object} state
|
||||
* @param {Object} params
|
||||
* @param {boolean} [params.isEmbedded]
|
||||
* @param {Object} [params.fields]
|
||||
* @param {string} [params.title]
|
||||
*/
|
||||
init: function (parent, state, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.isEmbedded = params.isEmbedded || false;
|
||||
this.title = params.title || '';
|
||||
this.fields = params.fields || {};
|
||||
this.modelName = params.modelName;
|
||||
this.startDate = params.startDate;
|
||||
this.endDate = params.endDate;
|
||||
this.titleField = params.titleField;
|
||||
this.progress_scale = params.progress_scale;
|
||||
this.child = params.child;
|
||||
this.childExist = params.childExist;
|
||||
this.disableLinking = params.disableLinking;
|
||||
this.chart = null;
|
||||
this.ganttId = _.uniqueId('gantt');
|
||||
this.$legendTooltip = null;
|
||||
this.$tooltip = null;
|
||||
this.rtlEnable = _t.database.parameters.direction == "rtl" ? true : false;
|
||||
},
|
||||
on_attach_callback: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.isInDOM = true;
|
||||
this._render();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
on_detach_callback: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.isInDOM = false;
|
||||
},
|
||||
_prepareGanttData: function(){
|
||||
var self = this;
|
||||
this.GanttChartData = {"data": [],"links": []};
|
||||
this.state.GanttData.forEach(function (data){
|
||||
var progress_bar = data.progress;
|
||||
if(self.progress_scale){
|
||||
progress_bar = (data.progress) / self.progress_scale;
|
||||
}
|
||||
self.GanttChartData.data.push({
|
||||
id: data.resId,
|
||||
start_date: data.startDate,
|
||||
end_date: data.endDate,
|
||||
text: data.titleField,
|
||||
type: data.type || '',
|
||||
progress: progress_bar ? progress_bar : 0,
|
||||
parent: data.parent ? data.parent : false,
|
||||
});
|
||||
});
|
||||
},
|
||||
async _renderView() {
|
||||
var self = this;
|
||||
this._prepareGanttData();
|
||||
if (this.chart) {
|
||||
|
||||
this.chart.clearAll();
|
||||
this.chart.parse(this.GanttChartData);
|
||||
this._changeState();
|
||||
}else{
|
||||
if (this.isInDOM) {
|
||||
this._renderTitle();
|
||||
gantt.plugins({
|
||||
tooltip: true
|
||||
});
|
||||
gantt.config.show_links = false;
|
||||
if(this.rtlEnable){
|
||||
gantt.config.rtl = true;
|
||||
}
|
||||
gantt.config.date_grid = _t.database.parameters.date_format;
|
||||
gantt.config.auto_scheduling = true;
|
||||
gantt.config.auto_scheduling_strict = true;
|
||||
gantt.i18n.setLocale(moment.locale());
|
||||
gantt.init('o_gantt_renderer');
|
||||
gantt.parse(this.GanttChartData);
|
||||
this.chart = gantt;
|
||||
this.chart.attachEvent("onBeforeLightbox", function(){
|
||||
return false;
|
||||
});
|
||||
this.chart.attachEvent("onAfterTaskDrag", function(id, mode, e){
|
||||
var currentTask = this.getTask(id);
|
||||
self._updateData(currentTask);
|
||||
});
|
||||
this._changeState();
|
||||
}
|
||||
}
|
||||
},
|
||||
_renderTitle: function () {
|
||||
if (this.title) {
|
||||
this.$el.prepend($('<label/>', {
|
||||
text: this.title,
|
||||
}));
|
||||
}
|
||||
},
|
||||
_changeState: function(){
|
||||
if(this.state.mode == 'month'){
|
||||
this.chart.config.scale_unit = "month";
|
||||
this.chart.config.date_scale = "%F, %Y";
|
||||
}else if(this.state.mode == 'week'){
|
||||
this.chart.config.scale_unit = "week";
|
||||
this.chart.config.date_scale = "Week #%W";
|
||||
}else{
|
||||
this.chart.config.scale_unit = "day";
|
||||
this.chart.config.date_scale = "%d %M";
|
||||
}
|
||||
this.chart.render();
|
||||
},
|
||||
_updateData: function(data){
|
||||
var updatedData = {};
|
||||
if(data.id.toString().startsWith('child_')){
|
||||
updatedData[this.child.startDate] = this.formatDate(data.start_date);
|
||||
updatedData[this.child.endDate] = this.formatDate(data.end_date);
|
||||
return this._rpc({
|
||||
model: this.child.model,
|
||||
method: 'write',
|
||||
args: [parseInt(data.id.substring("child_".length)), updatedData],
|
||||
});
|
||||
}else{
|
||||
updatedData[this.startDate] = this.formatDate(data.start_date);
|
||||
updatedData[this.endDate] = this.formatDate(data.end_date);
|
||||
return this._rpc({
|
||||
model: this.modelName,
|
||||
method: 'write',
|
||||
args: [data.id, updatedData],
|
||||
});
|
||||
}
|
||||
},
|
||||
formatDate: function(date){
|
||||
var d = new Date(date),
|
||||
month = '' + (d.getMonth() + 1),
|
||||
day = '' + d.getDate(),
|
||||
year = d.getFullYear();
|
||||
|
||||
if (month.length < 2)
|
||||
month = '0' + month;
|
||||
if (day.length < 2)
|
||||
day = '0' + day;
|
||||
|
||||
return [year, month, day].join('-');
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,171 @@
|
||||
flectra.define('web_gantt.GanttView', function (require) {
|
||||
"use strict";
|
||||
|
||||
|
||||
var AbstractView = require('web.AbstractView');
|
||||
var core = require('web.core');
|
||||
var GraphModel = require('web.GraphModel');
|
||||
var Controller = require('web_gantt.GanttController');
|
||||
var GanttRenderer = require('web_gantt.GanttRenderer');
|
||||
var GanttModel = require('web_gantt.GanttModel');
|
||||
var view_registry = require('web.view_registry');
|
||||
|
||||
var _t = core._t;
|
||||
var _lt = core._lt;
|
||||
|
||||
var searchUtils = require('web.searchUtils');
|
||||
|
||||
var GanttView = AbstractView.extend({
|
||||
display_name: _lt('Gantt'),
|
||||
icon: 'fa-tasks',
|
||||
config: _.extend({}, AbstractView.prototype.config, {
|
||||
Model: GanttModel,
|
||||
Controller: Controller,
|
||||
Renderer: GanttRenderer,
|
||||
}),
|
||||
viewType: 'gantt',
|
||||
searchMenuTypes: ['filter', 'groupBy', 'comparison', 'favorite'],
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
const additionalMeasures = params.additionalMeasures || [];
|
||||
let title;
|
||||
let startDate;
|
||||
let endDate;
|
||||
let progress;
|
||||
let measure;
|
||||
let progress_scale;
|
||||
let childExist = false;
|
||||
let child = {};
|
||||
const measures = {};
|
||||
const measureStrings = {};
|
||||
let groupBys = [];
|
||||
const groupableFields = {};
|
||||
this.fields.__count__ = { string: _t("Count"), type: 'integer' };
|
||||
|
||||
this.arch.children.forEach(field => {
|
||||
let fieldName = field.attrs.name;
|
||||
if (fieldName === "id") {
|
||||
return;
|
||||
}
|
||||
const interval = field.attrs.interval;
|
||||
if (interval) {
|
||||
fieldName = fieldName + ':' + interval;
|
||||
}
|
||||
if (field.attrs.type === 'start') {
|
||||
startDate = fieldName;
|
||||
}
|
||||
if(field.attrs.type === 'end'){
|
||||
endDate = fieldName;
|
||||
}
|
||||
if(field.attrs.type === 'title'){
|
||||
title = fieldName;
|
||||
}
|
||||
if(field.attrs.type === 'progress'){
|
||||
progress = fieldName;
|
||||
if(field.attrs.scale){
|
||||
progress_scale = parseInt(field.attrs.scale);
|
||||
}
|
||||
}
|
||||
if(field.attrs.type === 'child'){
|
||||
if(this.fields[fieldName].type == 'many2many' || this.fields[fieldName].type == 'one2many'){
|
||||
if(field.attrs.start && field.attrs.end){
|
||||
child['name'] = fieldName;
|
||||
child['startDate'] = field.attrs.start;
|
||||
child['endDate'] = field.attrs.end;
|
||||
child['model'] = this.fields[fieldName].relation;
|
||||
childExist = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (field.attrs.string) {
|
||||
measureStrings[fieldName] = field.attrs.string;
|
||||
}
|
||||
});
|
||||
|
||||
for (const name in measureStrings) {
|
||||
if (measures[name]) {
|
||||
measures[name].description = measureStrings[name];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove invisible fields from the measures
|
||||
this.arch.children.forEach(field => {
|
||||
let fieldName = field.attrs.name;
|
||||
if (field.attrs.invisible && py.eval(field.attrs.invisible)) {
|
||||
groupBys = groupBys.filter(groupBy => groupBy !== fieldName);
|
||||
if (fieldName in groupableFields) {
|
||||
delete groupableFields[fieldName];
|
||||
}
|
||||
if (!additionalMeasures.includes(fieldName)) {
|
||||
delete measures[fieldName];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const sortedMeasures = Object.values(measures).sort((a, b) => {
|
||||
const descA = a.description.toLowerCase();
|
||||
const descB = b.description.toLowerCase();
|
||||
return descA > descB ? 1 : descA < descB ? -1 : 0;
|
||||
});
|
||||
const countMeasure = {
|
||||
description: _t("Count"),
|
||||
fieldName: '__count__',
|
||||
groupNumber: 1,
|
||||
isActive: false,
|
||||
itemType: 'measure',
|
||||
};
|
||||
this.controllerParams.withButtons = params.withButtons !== false;
|
||||
this.controllerParams.measures = [...sortedMeasures, countMeasure];
|
||||
this.controllerParams.groupableFields = groupableFields;
|
||||
this.controllerParams.child = child;
|
||||
this.controllerParams.childExist = childExist;
|
||||
this.controllerParams.title = params.title || this.arch.attrs.string || _t("Untitled");
|
||||
// retrieve form and list view ids from the action to open those views
|
||||
// when the graph is clicked
|
||||
function _findView(views, viewType) {
|
||||
const view = views.find(view => {
|
||||
return view.type === viewType;
|
||||
});
|
||||
return [view ? view.viewID : false, viewType];
|
||||
}
|
||||
this.controllerParams.views = [
|
||||
_findView(params.actionViews, 'list'),
|
||||
_findView(params.actionViews, 'form'),
|
||||
];
|
||||
|
||||
this.rendererParams.fields = this.fields;
|
||||
this.rendererParams.modelName = params.modelName;
|
||||
this.rendererParams.startDate = startDate;
|
||||
this.rendererParams.endDate = endDate;
|
||||
this.rendererParams.progress = progress;
|
||||
this.rendererParams.child = child;
|
||||
this.rendererParams.childExist = childExist;
|
||||
this.rendererParams.progress_scale = progress_scale;
|
||||
this.rendererParams.titleField = 'display_name';
|
||||
this.rendererParams.title = this.arch.attrs.title; // TODO: use attrs.string instead
|
||||
this.rendererParams.disableLinking = !!JSON.parse(this.arch.attrs.disable_linking || '0');
|
||||
|
||||
this.loadParams.startDate = startDate;
|
||||
this.loadParams.endDate = endDate;
|
||||
this.loadParams.progress = progress;
|
||||
this.loadParams.child = child;
|
||||
this.loadParams.childExist = childExist;
|
||||
this.loadParams.titleField = 'display_name';
|
||||
this.loadParams.mode = this.arch.attrs.type || 'day';
|
||||
this.loadParams.orderBy = this.arch.attrs.order;
|
||||
this.loadParams.measure = measure || '__count__';
|
||||
this.loadParams.groupBys = groupBys;
|
||||
this.loadParams.fields = this.fields;
|
||||
this.loadParams.comparisonDomain = params.comparisonDomain;
|
||||
this.loadParams.stacked = this.arch.attrs.stacked !== "False";
|
||||
},
|
||||
});
|
||||
view_registry.add('gantt', GanttView);
|
||||
|
||||
return GanttView;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
<templates>
|
||||
|
||||
<t t-name="gantt_chart_popover_template">
|
||||
<div class="oe_tooltip_string" role="tooltip">
|
||||
<t t-esc="title"/>
|
||||
</div>
|
||||
<ul class="oe_tooltip_technical" role="tooltip">
|
||||
<li>
|
||||
<span class="oe_tooltip_technical_title">Start Date:</span>
|
||||
<t t-esc="startDate"/>
|
||||
</li>
|
||||
<li data-item="object">
|
||||
<span class="oe_tooltip_technical_title">End Date:</span>
|
||||
<t t-esc="endDate"/>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
<t t-name="GanttView.buttons">
|
||||
<button class="btn btn-primary">Add</button>
|
||||
<div class="btn-group" role="toolbar" aria-label="Change Mode">
|
||||
<button class="btn btn-secondary o_gantt_button" title="Month Mode" aria-label="Month Modet" data-mode="month">
|
||||
Month
|
||||
</button>
|
||||
<button class="btn btn-secondary o_gantt_button" title="Week Mode" aria-label="Week Mode" data-mode="week">
|
||||
Week
|
||||
</button>
|
||||
<button class="btn btn-secondary o_gantt_button" title="Day Mode" aria-label="Day Mode" data-mode="day">
|
||||
Day
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,19 @@
|
||||
<flectra>
|
||||
|
||||
<template id="assets_backend_gantt_chart" inherit_id="web.assets_backend" name="Website Backend Assets Gantt Chart">
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/web_gantt/static/src/dhtmlx/js/dhtmlxgantt.js"/>
|
||||
|
||||
<script type="text/javascript" src="/web_gantt/static/src/js/gantt_controller.js"/>
|
||||
<script type="text/javascript" src="/web_gantt/static/src/js/gantt_renderer.js"/>
|
||||
<script type="text/javascript" src="/web_gantt/static/src/js/gantt_model.js"/>
|
||||
<script type="text/javascript" src="/web_gantt/static/src/js/gantt_view.js"/>
|
||||
</xpath>
|
||||
<xpath expr="//link[last()]" position="after">
|
||||
<link rel="stylesheet" type="text/scss" href="/web_gantt/static/src/dhtmlx/css/dhtmlxgantt.css"/>
|
||||
|
||||
<link rel="stylesheet" type="text/scss" href="/web_gantt/static/src/css/style.css"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</flectra>
|
||||
Reference in new issue
Block a user