/*
* Copyright 2020 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* CostModeling tool
* @private
*/
Ext.define('Ametys.plugins.odf.pilotage.tool.CostModelingTool', {
extend: 'Ametys.plugins.odf.tree.AbstractODFTreeGridTool',
statics: {
/**
* Copy the value in the given target column.
* @param {String} targetColumnDataIndex The data index of the target column.
* @param {String} targetColumnStateId The state id of the target column.
* @param {String} contentId The id of the content.
* @param {String} title The title of the content.
* @param {Number} targetCurrentValue The current value in the targeted cell.
* @param {Object} valueToCopy The value to copy.
* @static
*/
copyPreviousData: function(targetColumnDataIndex, targetColumnStateId, contentId, title, targetCurrentValue, valueToCopy)
{
// If the new value to copy differs from the previous value in the targeted cell, copy it
if (targetCurrentValue != valueToCopy)
{
var tool = Ametys.tool.ToolsManager.getTool('uitool-simulator');
var store = tool._treePanel.getStore();
var record = store.findRecord("contentId", contentId);
record.set(targetColumnDataIndex, valueToCopy);
tool._afterEdit(record, targetColumnStateId, contentId, title, valueToCopy.toFixed());
}
}
},
/**
* @property {Object} _overriddenData
* @private
*/
constructor: function(config)
{
this._overriddenData = {};
this.callParent(arguments);
},
sendCurrentSelection: function()
{
// The tool does not react to selection
},
_getPlugins: function()
{
return [
this._getEditPlugin()
];
},
/**
* Get all data overridden by the user
* @return overridden data
*/
_getOverriddenData: function()
{
return this._overriddenData;
},
_createTree: function()
{
return Ext.create('Ametys.plugins.odf.pilotage.tool.CostModelingTreeGridPanel', this._getTreeConfig());
},
_getRootNodeInfo: function()
{
return Ext.apply(this.callParent(arguments), {
catalog: this.getParams().catalog || this._treePanel.getCatalog(),
lang: this.getParams().lang || this._treePanel.getLang(),
overriddenData: this._getOverriddenData()
});
},
_getTreeConfig: function()
{
var treeCfg = this.callParent(arguments);
return Ext.apply(treeCfg, {
activeIndicators: ["odf-indicator-code", "odf-indicator-shared-status", "odf-indicator-courselist-type", "odf-indicator-stepholder"],
natures: this.getInitialConfig()["enseignement-natures"]
});
},
/**
* @protected
* Update the store and refresh the tool
*/
_customRefresh: function()
{
this.showRefreshing();
var contentsToRefresh = [];
var rootContentId;
// Fill a map gathering all contents to refresh with their path
for (var key in this._treePanel.store.getData().map) {
var value = this._treePanel.store.getData().map[key];
if (value.isRoot())
{
// The root node
rootContentId = value.data.contentId;
}
// The path of the content
var path = value.getPath("name").substring(1)
// The ID of the content
var contentId = value.data.contentId;
// Each content is stored with his path
contentsToRefresh.push({namePath: path, contentId: contentId, path: value.getPath("contentId", ";").substring(1).split(";")});
}
Ametys.data.ServerComm.callMethod({
role: this.getInitialConfig()["serverRole"] || "org.ametys.plugins.odfpilotage.helper.CostComputationTreeHelper",
methodName: "refresh",
parameters: [contentsToRefresh, rootContentId, this.getParams().catalog, this.getParams().lang, this._getOverriddenData()],
callback: {
handler: this._customRefreshCb,
scope: this
}
});
},
/**
* @protected
* Callback function called after #_customRefresh is processed.
* Save new values of each record in the store
* @param {Object} refreshedData a map containing all new values of the opened contents
*/
_customRefreshCb: function(refreshedData)
{
// To avoid useless calls to renderers
this._treePanel.store.suspendEvents();
for (var key in refreshedData)
{
var data = refreshedData[key];
// Retrieve the record of the current content
var record = this._getRecord(key);
// Effectives
record.set("effectives", data.effectives);
record.set("effectivesGlobal", data.effectives);
record.set("effectivesLocal", data.effectives);
record.set("effectivesType", data.effectives);
// Groups
record.set("groups", data.groups);
record.set("groupsDisplay", data.groups);
record.set("groupType", data.groups);
// Current year and Last year data
record.set("previousData", data.previousData);
record.set("currentYearEffectives", data.previousData);
record.set("currentYearGroups", data.previousData);
record.set("precedingYearEffectives", data.previousData);
record.set("precedingYearGroups", data.previousData);
// Volume of hours
record.set("volumesOfHours", data.volumesOfHours);
for (var nature of Object.keys(this.getInitialConfig()["enseignement-natures"]))
{
record.set(nature, data.volumesOfHours);
}
// EqTD
record.set("eqTDglobal", data.eqTD);
record.set("eqTDprorated", data.eqTD);
record.set("eqTDlocal", data.eqTD);
// H/E ratio
record.set("heRatio", data.heRatio);
record.commit();
}
// Launch all renderers at the end
this._treePanel.store.resumeEvents();
this._treePanel.lockedGrid.getView().refresh();
this._treePanel.normalGrid.getView().refresh()
this.showRefreshed();
this._treePanel.getDockedComponent("costModelingHint").setVisible(false);
},
/**
* @protected
* Retrieve the record of a content by a path
* @param {string} path the path of the content
* @return {Object} the record
*/
_getRecord: function(path)
{
for (var key in this._treePanel.store.getData().map)
{
var value = this._treePanel.store.getData().map[key];
if (value.getPath("name").substring(1) == path)
{
return value;
}
}
},
/**
* @private
* Create the plugin instance to edit the grid
* @return {Ext.grid.plugin.CellEditing} The edit plugin
*/
_getEditPlugin: function()
{
return Ext.create('Ext.grid.plugin.CellEditing', {
id: "edit",
clicksToEdit: 1,
editAfterSelect: false,
moveEditorOnEnter: true,
listeners: {
'beforeedit': Ext.bind(this._beforeEdit, this),
'edit': Ext.bind(this._edit, this),
'validateedit': Ext.bind(this._validateEdit, this)
}
});
},
/**
* Return true if current record is a CoursePart
* @param {Object} record the record to check
*/
isCoursePart: function(record)
{
var contenttypes = record.data.contenttypesIds;
return contenttypes.includes('org.ametys.plugins.odf.Content.coursePart')
},
/**
* Check right before edition
* @param {Ext.grid.plugin.CellEditing} editor The editor plugin
* @param {Object} e An edit event
* @private
*/
_beforeEdit: function(editor, e)
{
var contenttypes = e.record.data.contenttypesIds;
var columnId = e.column.stateId;
// Editable if the user click on the groups or volume of hours of a course part
if (this.isCoursePart(e.record))
{
var enseignementNature = this.getInitialConfig()["enseignement-natures"][columnId];
return columnId == 'groups' || enseignementNature !== undefined && e.record.data.volumesOfHours[enseignementNature.id] !== undefined;
}
// Editable if the user click on the global effective column of a program, subprogram, step or course
if (contenttypes.includes('org.ametys.plugins.odf.Content.course')
|| contenttypes.includes('org.ametys.plugins.odf.Content.container')
|| contenttypes.includes('org.ametys.plugins.odf.Content.subProgram')
|| contenttypes.includes('org.ametys.plugins.odf.Content.program')
)
{
return columnId == 'effectivesGlobal';
}
return false;
},
/**
* When an edition ends, cancel the change if the value is not a conform value
* @param {Ext.grid.plugin.CellEditing} editor The editor plugin
* @param {Object} e An edit event
* @private
*/
_edit: function(editor, e)
{
if (e.record.data[e.field] < 0 || (!Object.keys(this.getInitialConfig()["enseignement-natures"]).includes(e.field) && !Number.isInteger(e.record.data[e.field])))
{
e.record.set(e.field, e.record.modified[e.field]);
}
},
/**
* Save overridden data by the user and check if the entered value is a positive number
* @param {Ext.grid.plugin.CellEditing} editor The editor plugin
* @param {Object} context The editing context
* @private
*/
_validateEdit: function (editor, context)
{
var columnId = context.column.stateId;
var value = context.value;
var contentId = context.record.data.contentId;
if (value != undefined && context.originalValue != value && value >= 0)
{
var isVolumeOfHours = Object.keys(this.getInitialConfig()["enseignement-natures"]).includes(columnId);
if ((!isVolumeOfHours && Number.isInteger(value)) || isVolumeOfHours)
{
var title = context.record.data.title;
if (isVolumeOfHours)
{
this._afterEdit(context.record, 'nbHours', contentId, title, value.toFixed(2));
}
else
{
this._afterEdit(context.record, columnId, contentId, title, value.toString());
}
}
}
},
/**
* Do after edit actions like setting dirty to true and adding an edited hint
* @param {Object} record the record being edited
* @param {String} columnId The id of the column
* @param {String} contentId The id of the content
* @param {String} title The title of the content
* @param {String} value The value to set in overriddenData
*/
_afterEdit: function(record, columnId, contentId, title, value)
{
this._treePanel.getDockedComponent("costModelingHint").setVisible(true);
this.setDirty(true);
if (!this._overriddenData[contentId])
{
this._overriddenData[contentId] = {};
this._overriddenData[contentId]['title'] = title;
}
this._overriddenData[contentId][columnId] = value;
},
/**
* Get node from contentId
* @param {String} contentId id content
*/
getCurrentNode: function(contentId)
{
return this._treePanel.getStore().getData().items.filter(i=>i.data.contentId == contentId);
},
/**
* Get parent node in tree for content with current id
* @param {Object} currentNode node to get parent from
*/
getParentNode: function(currentNode)
{
return this._treePanel.getStore().getNodeById(currentNode.getData().parentId);
}
});