Commit 8b78c1a7 authored by Sebastien Robin's avatar Sebastien Robin

mrp: add Gantt chart of manufacturing available in renderjs interface

parent 2f00c84c
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>manufacturing_order_module_view_gantt</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>2.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Manufacturing Order Module Gantt</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/ManufacturingOrderModule_viewGantt</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>manufacturing_gantt</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ManufacturingOrderModule_viewGantt</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Delivery_viewPlanning</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Planning</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>default</string>
<string>gadget_url</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>manufacturing_gantt</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Manufacturing Gantt</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: {\'portal_type\': ["Manufacturing Order", "Manufacturing Execution"],}</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python:\'gadget_officejs_manufacturing_gantt.html\'</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -12,6 +12,7 @@ Manufacturing Order Line | jump_to_transformation
Manufacturing Order Line | price_view
Manufacturing Order Line | quantity_view
Manufacturing Order Line | view
Manufacturing Order Module | manufacturing_order_module_view_gantt
Manufacturing Order Module | view
Manufacturing Order | jump_to_related_internal_packing_list
Manufacturing Order | jump_to_related_manufacturing_execution
......
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html>
<head>
<title>Gantt Interface</title>
</head>
<body>
<h1>widget_gantt_interface</h1>
<h3>A Gantt gadget allows to display various types of schedules using Gantt diagrams</h3>
<dl>
<dt>render</dt>
<dd>render a Gantt diagram</dd>
<dl>
<dt data-parameter-required="required" data-parameter-type="object">configuration_dict</dt>
<dd><code style="display:block;white-space:pre-wrap">
Generic timeline gadget. The purpose of this gadget is to provide an unique
API for various gantt libraries
Options supported are :
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Parameters to generate a gantt",
"properties": {
"data_list": {
"description": "the list of task to be displayed in timeline",
"items": {
"properties" : {
"title": {
"description": "The title of the task",
"type": "string"
},
"id": {
"description": "an identifier for the task",
"type": "string"
},
"parent_id": {
"description": "the identifier of the corresponding parent, this is optional for top level task",
"type": "string"
},
"start_date": {
"description": "task starting date",
"type": "string"
},
"stop_date": {
"description": "task end date",
"type": "string"
},
"background_color": {
"description": "task color",
"type": "string"
},
"type": {
"description": "type of task. project is for a task depending on subtast, milestone represent a milestone, and task is a standard task",
"type": "string",
"default": "task",
"enum": ["task", "project", "milestone"]
},
},
"additionalProperties": false,
"type": "object",
},
"type": "array",
}
},
"additionalProperties": false
}
Example of options:
{ data_list : [{'id': '1',
'title': 'Phase 1',
'start_date': '2017-03-01',
'stop_date': '2017-04-15',
'type': 'project'},
{'id': '1.1',
'parent_id': '1',
'title': 'Task A in Phase 1',
'start_date': '2017-03-01',
'stop_date': '2017-04-10',
'type': 'task'},
{'id': '1.2',
'parent_id': '1.1',
'title': 'Task B in Phase 1',
'start_date': '2017-04-01',
'stop_date': '2017-04-15',
'type': 'task'},
{'id': '2',
'title': 'Phase 2',
'start_date': '2017-04-15',
'stop_date': '2017-06-30',
'type': 'task'}]
}
</code></dd>
</dl>
</dl>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>ERP5 Widget Timeline with Vis</title>
<!-- interfaces -->
<link rel="http://www.renderjs.org/rel/interface" href="gadget_officejs_interface_widget_gantt.html">
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<!-- custom script -->
<script src="gadget_officejs_manufacturing_gantt.js" type="text/javascript"></script>
</head>
<body>
<div class="gantt-content">
</div>
</body>
</html>
\ No newline at end of file
/*global window, rJS, RSVP, console */
/*jslint nomen: true, indent: 2 */
(function (window, rJS, RSVP) {
"use strict";
/////////////////////////////////////////////////////////////////
// templates
/////////////////////////////////////////////////////////////////
var gadget_klass = rJS(window);
/////////////////////////////////////////////////////////////////
// some methods
/////////////////////////////////////////////////////////////////
gadget_klass
/////////////////////////////////////////////////////////////////
// ready
/////////////////////////////////////////////////////////////////
.ready(function (gadget) {
gadget.property_dict = {};
})
/////////////////////////////////////////////////////////////////
// published methods
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// acquired methods
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (option_dict) {
var gadget = this,
container,
graph_data_and_parameter,
chart;
gadget.property_dict.option_dict = option_dict.value;
console.log("gantt option_dict", option_dict.value);
gadget.renderGantt(); //Launched as service, not blocking
})
.declareJob("renderGantt", function () {
var gadget = this,
option_dict = gadget.property_dict.option_dict;
return gadget.declareGadget(
"unsafe/gadget_officejs_widget_gantt_dhtmlx.html",
{scope: "gantt",
sandbox: "iframe",
element: gadget.element.querySelector(".gantt-content")
})
.push(function (gantt_widget) {
// First search all production report not finished to find out the
// list of production orders still having work on them
var query;
gadget.property_dict.gantt_widget = gantt_widget;
console.log("gantt_widget", gantt_widget);
query = 'portal_type:="Manufacturing Execution" AND NOT simulation_state: ("draft", "cancelled", "delivered")';
return gadget.jio_allDocs({
query: query,
limit: 10000,
sort_on: [['delivery.start_date', 'ascending']],
select_list: ['reference', 'title', 'start_date', 'stop_date', 'uid', 'causality_uid']
});
})
.push(function (delivery_list) {
var causality_uid_list = [0], // Initiliaze with 0 to make sure to have at least one uid to search for
i, delivery, query;
delivery_list = delivery_list.data.rows;
for (i = 0; i < delivery_list.length; i = i + 1) {
delivery = delivery_list[i].value;
if (causality_uid_list.indexOf(delivery.causality_uid) === -1) {
causality_uid_list.push(delivery.causality_uid);
}
}
query = 'portal_type:="Manufacturing Execution" AND causality_uid: (' + causality_uid_list.join(', ') + ') AND NOT simulation_state: ("draft", "cancelled")';
console.log("QUERY", query);
return gadget.jio_allDocs({
query: query,
limit: 10000,
sort_on: [['delivery.start_date', 'ascending']],
select_list: ['reference', 'title', 'start_date', 'stop_date', 'uid', 'causality_uid', 'causality_title']
});
})
.push(function (delivery_list) {
var i, delivery, causality_list = [],
causality_dict = {}, causality_data,
gantt_data = {},
tree_list = [],
data_list = [],
sale_order_uid,
delivery_data, tree_data;
delivery_list = delivery_list.data.rows;
for (i = 0; i < delivery_list.length; i = i + 1) {
delivery = delivery_list[i].value;
if (delivery.causality_uid !== undefined) {
if (causality_list.indexOf(delivery.causality_uid) === -1) {
causality_list.push(delivery.causality_uid);
}
causality_data = causality_dict[delivery.causality_uid] || {'start_date': new Date(delivery.start_date),
'stop_date': new Date(delivery.stop_date),
'title': delivery.causality_title,
'type': 'project',
'id': delivery.causality_uid};
causality_data.start_date = new Date(Math.min.apply(
null, [causality_data.start_date, new Date(delivery.start_date)]));
causality_data.stop_date = new Date(Math.max.apply(
null, [causality_data.stop_date, new Date(delivery.stop_date)]));
causality_dict[delivery.causality_uid] = causality_data;
}
if (i === 0) {
// We assume that by the sort on order_reference that the first line is a level 1 line
sale_order_uid = delivery.parent_uid;
}
if (delivery.start_date !== undefined && delivery.stop_date !== undefined) {
delivery_data = {'title': delivery.title,
'id': delivery.uid,
//'tree_id': delivery.uid,
'parent_id': delivery.causality_uid,
'start_date': delivery.start_date,
'stop_date': delivery.stop_date};
if (delivery.parent_uid !== sale_order_uid) {
delivery_data.parent_id = delivery.parent_uid;
}
data_list.push(delivery_data);
}
}
for (i = 0; i < causality_list.length; i = i + 1) {
causality_data = causality_dict[causality_list[i]];
data_list.push(causality_data);
}
gantt_data.data_list = data_list;
console.log("gantt_data", gantt_data);
return gadget.property_dict.gantt_widget.render(gantt_data);
});
});
}(window, rJS, RSVP));
\ No newline at end of file
/*global window, rJS, RSVP, console, gantt */
/*jslint nomen: true, indent: 2 */
(function (window, rJS, RSVP, gantt) {
"use strict";
/////////////////////////////////////////////////////////////////
// templates
/////////////////////////////////////////////////////////////////
var gadget_klass = rJS(window);
var transformDateStringToDhtmlxDate = function (date_string) {
var my_date;
my_date = new Date(date_string);
return "" + my_date.getDate() + "-" + (my_date.getMonth() + 1) + "-" + my_date.getFullYear();
};
var getGanttDataAndParameterFromConfiguration = function (configuration_dict) {
var gantt_configuration = {},
data_list = configuration_dict.data_list,
task,
data,
start_date,
stop_date,
owner_section_dict = {name: "owner", height: 22, map_to: "owner", type: "select", options: []},
i;
gantt_configuration.data = {};
gantt_configuration.data.data = [];
gantt_configuration.start_date_list = [];
gantt_configuration.stop_date_list = [];
/*
{name: "description", height: 38, map_to: "text", type: "textarea", focus: true},
{name: "owner", height: 22, map_to: "owner", type: "select", options: [
{key:"0", label: ""},
{key:"1", label: "Mark"},
{key:"2", label: "John"},
{key:"3", label: "Rebecca"},
{key:"4", label: "Alex"}]},
{name: "time", type: "duration", map_to: "auto", time_format:["%d","%m","%Y","%H:%i"]}
];*/
for (i = 0 ; i < data_list.length ; i = i + 1) {
task = {};
data = data_list[i];
task.id = data.id;
task.text = data.title;
// Be Careful, DHTMLX seems less and less open source, and nice features when we
// have a type "project" is unfortunately available only with paid license. Though
// We will still use it to set a different color for this kind of task
if (data.type === 'project') {
task.type = gantt.config.types.project;
}
task.start_date = transformDateStringToDhtmlxDate(data.start_date);
start_date = new Date(data.start_date);
gantt_configuration.start_date_list.push(start_date);
stop_date = new Date(data.stop_date);
gantt_configuration.stop_date_list.push(stop_date);
task.duration = (stop_date - start_date) / (86400 * 1000);
if (data.parent_id !== undefined) {
task.parent = data.parent_id;
}
task.open = true;
console.log("data", data);
console.log("adding task", task);
gantt_configuration.data.data.push(task);
}
return gantt_configuration;
};
/////////////////////////////////////////////////////////////////
// some methods
/////////////////////////////////////////////////////////////////
gadget_klass
/////////////////////////////////////////////////////////////////
// ready
/////////////////////////////////////////////////////////////////
.ready(function (gadget) {
gadget.property_dict = {};
})
/////////////////////////////////////////////////////////////////
// published methods
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// acquired methods
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (configuration_dict) {
var gadget = this,
min_start_date, max_stop_date, day_difference,
gantt_configuration;
//graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(configuration_dict);
//console.log("graph_data_and_parameter", graph_data_and_parameter);
//chart = new Chart(container, graph_data_and_parameter);
//gadget.property_dict.chart = chart;
console.log("configuration_dict", configuration_dict);
gantt_configuration = getGanttDataAndParameterFromConfiguration(configuration_dict);
gantt.init("dhtmlx-gantt");
//gantt.parse(tasks);
console.log("gantt_configuration", gantt_configuration);
gantt.config.columns = [{
name: "text",
label: "Task Name",
width: "200",
tree: true
}, {
name: "start_date",
label: "Start Date",
/*template: function (obj) {
return gantt.getLabel("owner", obj.owner);
},*/
align: "center"
//width: 140
}, {
name: "duration",
label: "Duration",
/*template: function (obj) {
return gantt.getLabel("owner", obj.owner);
},*/
align: "center"
//width: 140
}
];
// To change column based on an color_id_attribute
gantt.templates.task_class = function (start, end, obj) {
var class_name = "task";
if (obj.type === gantt.config.types.project) {
class_name = "project";
}
return class_name;
};
gantt.templates.scale_cell_class = function (date) {
if (date.getDay() === 0 || date.getDay() == 6) {
return "weekend";
}
};
gantt.templates.task_cell_class = function (item, date) {
if (date.getDay() === 0 || date.getDay() == 6) {
return "weekend";
}
};
// Customize scales depending on min start date and max stop date
if (gantt_configuration.start_date_list.length > 0) {
min_start_date = new Date(Math.min.apply(null, gantt_configuration.start_date_list));
max_stop_date = new Date(Math.max.apply(null, gantt_configuration.stop_date_list));
day_difference = (max_stop_date - min_start_date) / (1000 * 60 * 60 * 24);
if (day_difference > (365 * 2)) {
// switch to year scale
gantt.config.min_column_width = 30;
gantt.config.scale_unit = "year";
gantt.config.date_scale = "%Y";
gantt.config.scale_height = 60;
gantt.config.subscales = [{
unit: "month",
step: 2,
date: "#%M"
}];
} else if (day_difference > 30) {
gantt.config.scale_unit = "day";
gantt.config.duration_unit = "day";
gantt.config.date_scale = "%d";
gantt.config.subscales = [
{unit: "month", step: 1, date: "%F, %Y"}
];
gantt.config.scale_height = 50;
gantt.config.min_column_width = 30;
}
}
// Interaction when a box is moved. Only for demo purpose, we might
// later find some ways to save changes
gantt.attachEvent("onAfterTaskDrag", function (id, mode) {
var task = gantt.getTask(id);
if (mode == gantt.config.drag_mode.progress) {
var pr = Math.floor(task.progress * 100 * 10) / 10;
gantt.message(task.text + " is now " + pr + "% completed!");
} else {
var convert = gantt.date.date_to_str("%H:%i, %F %j");
var s = convert(task.start_date);
var e = convert(task.end_date);
gantt.message(task.text + " starts at " + s + " and ends at " + e);
}
});
gantt.parse(gantt_configuration.data);
});
}(window, rJS, RSVP, gantt));
\ No newline at end of file
......@@ -2,6 +2,7 @@ web_page_module/officejs_todomvc_*
image_module/officejs_todomvc_*
web_site_module/officejs_todomvc
image_module/officejs_audioplayer_*
web_page_module/dhtmlx_gantt_*
image_module/wallsearch_icon_svg
web_page_module/officejs_audioplayer_*
web_page_module/gadget_field_*
......
......@@ -6,6 +6,7 @@ image_module/officejs_audioplayer_*
image_module/officejs_todomvc_*
image_module/wallsearch_icon_svg
web_page_module/ckeditor_*
web_page_module/dhtmlx_gantt_*
web_page_module/fb_sdk_js
web_page_module/gadget_ckeditor_*
web_page_module/gadget_field_*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment