Compare commits

...

57 Commits

Author SHA1 Message Date
d5c81668e3 set tooltip on event 2020-07-31 14:33:57 +02:00
f82279e9b2 ajout d'un utilisateur fictif lors de la création d'un service, et affichage des events par service 2020-07-31 14:33:54 +02:00
6b0b8bf281 ajout possibilité de sélectionner plusieurs utilisateurs 2020-07-31 14:14:44 +02:00
5c690431dc Ajout infromations export 2020-07-31 10:59:58 +02:00
8dee5b5ce4 export journée pleines travaillés 2020-07-31 10:44:19 +02:00
c6ade61a08 Ajout d'une page unique pour les exports 2020-07-31 10:44:19 +02:00
1f2b36b7fd correction bug affiche external trip 2020-07-31 10:43:51 +02:00
016d209d43 Merge branch 'referent_projet' into timetracking 2020-07-30 13:57:36 +02:00
f54f582d25 Merge branch 'changehourevent' into timetracking 2020-07-30 13:57:18 +02:00
f183d744cc correction externaltrip si penalty 2020-07-30 10:56:21 +02:00
756f946ce8 affichage des affectations sur le profil utilisateur 2020-07-30 10:26:39 +02:00
9604ce80ef list les référents projet dans un tooltip 2020-07-30 10:06:00 +02:00
d4b05c883c Ajout icone congé sur calendrier 2020-07-30 09:06:44 +02:00
aeeb6384c4 ajout icone congés 2020-07-30 09:06:44 +02:00
8b46636159 correction bug 2020-07-30 09:06:44 +02:00
9471ada755 ajout propriété déplacement externe 2020-07-30 09:06:44 +02:00
df4d62df7d bug correction 2020-07-30 09:06:44 +02:00
8b4d1e31ec disable hour edition if more than one day 2020-07-30 09:06:44 +02:00
dc182b6888 ajout possibilité de modifier la plage horaire d'un événement sur le calendrier 2020-07-30 09:06:44 +02:00
99b55dece3 set default active timer view 2020-07-30 09:05:06 +02:00
cbe0cbf4e7 Ajout des horaires de travail en paramètres .env 2020-07-30 09:05:06 +02:00
c30bd74e6f Passage d'un timer en heure supplémentaire si hors des plages horaires de travail 2020-07-30 09:05:06 +02:00
80b6e4964f correciton calcul de la durée automatiquement 2020-07-30 09:05:06 +02:00
1fb5c4c715 ajout page validation des timer et option heure supplémentaires 2020-07-30 09:05:05 +02:00
a316a4c753 ajout numéro de semaine dans les tableau de synthèse 2020-07-30 09:05:05 +02:00
46805a2e3d export astreintes actives et exports 2020-07-30 09:05:05 +02:00
2513eced52 correction affectation utilisateur, et ajout vue caendrier 2020-07-30 09:05:05 +02:00
38b8b79fb1 timer description no mandatory 2020-07-30 09:05:05 +02:00
78b6651483 suppression de public/build dans gitignore 2020-07-30 09:05:05 +02:00
8f5f136607 Time Tracking 2020-07-30 09:05:05 +02:00
0ba2f94521 add spacing to navbar at bottom 2020-07-30 09:04:28 +02:00
0ee945eed3 update report csv format 2020-07-28 10:44:00 +02:00
d92e1d8d5c rapport hebdomadaire, par projet, tâche et utilisateur 2020-07-28 10:36:44 +02:00
25f78e3f34 set correct report format 2020-07-28 09:41:34 +02:00
6452f94cfe Rapport hebdo par utilisateur 2020-07-27 15:22:16 +02:00
5a4e0c6c0b typo 2020-06-26 11:57:09 +02:00
19889197ce update comoser.lock 2020-06-26 11:54:01 +02:00
ee6601bc35 add penalty to export 2020-06-19 13:54:14 +02:00
7fbed84998 Ajout d'un export csv hebdomadaire sur les rapports 2020-06-19 11:56:33 +02:00
32c08bcb52 remove debug infos 2020-06-18 11:42:27 +02:00
dd4f03a00e correction sur la plage horaire de récupération des événements 2020-06-18 11:41:36 +02:00
ae78edf02f correciton css pour affichage username calendrier 2020-06-18 11:09:37 +02:00
00fc14c91b affichage utilisateur sur calendrier 2020-06-18 10:36:09 +02:00
5643360f9d modification mise en page du rapport 2020-06-17 17:32:32 +02:00
7f19e8c5ff Affichage cumul par semaines 2020-06-17 17:32:19 +02:00
7edbdbd3e7 présentaiton sous forme de tableau des consommation passé dans les rapports 2020-06-17 14:48:18 +02:00
f944981109 Renommage Proposition en Commande 2020-06-16 11:26:11 +02:00
6c68abf922 Merge branch 'notification' of afornerot/schedule into master 2020-06-16 11:16:01 +02:00
79b8fb9af5 add dependencies 2020-06-16 11:11:19 +02:00
efa4f1fc62 Envoie des notifications à tous les valideurs, et variable pour messagerie globale de notification 2020-06-12 16:58:23 +02:00
root
6b00cec84b resolution cumul sur total planifié (ref #4) 2020-06-12 15:29:27 +02:00
20d42d319a Envoie de notification à la pose et la vadliation de congés 2020-06-12 15:18:53 +02:00
root
80227e7416 suppression dump 2020-06-11 11:52:19 +02:00
root
c948be02cc suppression dump 2020-06-11 11:49:27 +02:00
08249abbf5 controle sur suppression 2020-06-10 10:22:47 +02:00
03680beba1 Merge branch 'master' of https://forge.cadoles.com/afornerot/schedule 2020-05-13 14:03:59 +02:00
1eafb7090e genkey sans message au reconfigure 2020-05-13 14:03:46 +02:00
56 changed files with 4253 additions and 184 deletions

View File

@@ -6,7 +6,9 @@
<containers> <containers>
<container name='web'> <container name='web'>
<!--
<package>schedule-apps</package> <package>schedule-apps</package>
-->
<!-- service de configuration apache --> <!-- service de configuration apache -->
<service method="apache" servicelist="schedule">schedule</service> <service method="apache" servicelist="schedule">schedule</service>
@@ -32,6 +34,7 @@
<variable type='string' name='schedule_allow_hosts' description="Hôtes authorisés à utiliser la base de données" multi='True'/> <variable type='string' name='schedule_allow_hosts' description="Hôtes authorisés à utiliser la base de données" multi='True'/>
<variable type='string' name='schedule_dbuser' description='Utilisateur du serveur de base de données'/> <variable type='string' name='schedule_dbuser' description='Utilisateur du serveur de base de données'/>
<variable type='string' name='schedule_dbpass' description='Fichier de mot de passe du serveur'/> <variable type='string' name='schedule_dbpass' description='Fichier de mot de passe du serveur'/>
<variable type='mail' name='schedule_email_global_notif' description='Email pour envoie de notifications'/>
</family> </family>
</variables> </variables>

View File

@@ -32,6 +32,16 @@ APP_NAME=Schedule
APP_ENV=PROD APP_ENV=PROD
APP_CRON=false APP_CRON=false
# Office hours
OFFICE_HOUR_START=09:00
OFFICE_HOUR_END=17:30
# MAIL sendmail / smtp
MAILER_METHOD=sendmail
MAILER_URL=
MAILER_NOREPLY=noreply@noreply.fr
MAILER_DEFAULT_NOTIF=
# BDD # BDD
DATABASE_NAME= DATABASE_NAME=
DATABASE_USER= DATABASE_USER=
@@ -46,3 +56,9 @@ CAS_USERNAME=username
CAS_EMAIL=email CAS_EMAIL=email
CAS_LASTNAME=lastname CAS_LASTNAME=lastname
CAS_FIRSTNAME=firstname CAS_FIRSTNAME=firstname
###> symfony/swiftmailer-bundle ###
# For Gmail as a transport, use: "gmail://username:password@localhost"
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
# Delivery is disabled by default via "null://localhost"
MAILER_URL=

View File

@@ -17,7 +17,6 @@
###> symfony/webpack-encore-bundle ### ###> symfony/webpack-encore-bundle ###
/node_modules/ /node_modules/
/public/build/
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
###< symfony/webpack-encore-bundle ### ###< symfony/webpack-encore-bundle ###

View File

@@ -0,0 +1,609 @@
/* Creates a new Task object. */
function Task(id ,name, description) {
this._name = name;
this._taskid = id;
this._description = description;
this._state = Task.State.STOPPED;
this._timeSpentInPreviousIterations = 0;
this._startDate = 0;
this._endDate = 0;
this._currentIterationStartTime = 0;
this._customTimeSpent = 0;
this._onChange = null;
}
/* Possible task states. */
Task.State = {
STOPPED: "stopped",
RUNNING: "running"
}
Task.prototype = {
/* Returns the task name. */
getName: function() {
return this._name;
},
/* Returns the task description. */
getDescription: function() {
if (this._description == "NaN") {
this._description = ""
}
return this._description;
},
/* Returns the task state. */
getState: function() {
return this._state;
},
/* Is the task stopped? */
isStopped: function() {
return this._state == Task.State.STOPPED;
},
/* Is the task running? */
isRunning: function() {
return this._state == Task.State.RUNNING;
},
/*
* Sets the "onChange" event handler. The "onChange" event is fired when the
* task is started, stopped, or reset.
*/
setOnChange: function(onChange) {
this._onChange = onChange;
},
/*
* Returns the time spent on the task in the current work iteration. Works
* correctly only when the task is running.
*/
_getCurrentIterationTime: function() {
return (new Date).getTime() - this._currentIterationStartTime;
},
/*
* Returns the total time spent on the task. This includes time spent in
* the current work iteration if the task is running.
*/
getTimeSpent: function() {
var result = this._timeSpentInPreviousIterations;
if (this._state == Task.State.RUNNING) {
result += this._getCurrentIterationTime();
}
return result;
},
/* Calls the "onChange" event handler if set. */
_callOnChange: function() {
if (typeof this._onChange == "function") {
this._onChange();
}
},
/* Starts a new task work iteration. */
start: function() {
if (this._state == Task.State.RUNNING) { return };
if (this._startDate == 0) {this._startDate = new Date()}
this._state = Task.State.RUNNING;
this._currentIterationStartTime = (new Date).getTime();
this._callOnChange();
},
/* Stops the current task work iteration. */
stop: function() {
if (this._state == Task.State.STOPPED) { return };
this._state = Task.State.STOPPED;
this._timeSpentInPreviousIterations += this._getCurrentIterationTime();
this._currentIterationStartTime = 0;
this._endDate = new Date();
this._callOnChange();
},
/* Stops the current task work iteration and resets the time data. */
reset: function() {
this.stop();
this._timeSpentInPreviousIterations = 0;
this._callOnChange();
},
save: function(task){
console.log(task)
$.ajax({
type: "POST",
data: {
taskname: task._name,
taskid: task._taskid,
description: task._description,
start: task._startDate,
end: task._endDate,
duration: task._timeSpentInPreviousIterations,
},
url: "{{ path('app_timer_create') }}",
success: function (response) {
response=JSON.parse(response);
if(response.return=="KO") {
$("#dataTable_wrapper").append("<div class='alert alert-danger' style='margin: 5px 0px'>"+response.error+"</div>");
}else{
location.reload();
}
}
});
},
/* Serializes the task into a string. */
serialize: function() {
/*
* Originally, I wanted to use "toSource" and "eval" for serialization and
* deserialization, but "toSource" is not supported by WebKit, so I resorted
* to ugly hackery...
*/
return [
encodeURIComponent(this._name),
this._state,
this._timeSpentInPreviousIterations,
this._currentIterationStartTime,
this._startDate,
this._endDate,
this._taskid,
this._description,
].join("&");
},
/* Deserializes the task from a string. */
deserialize: function(serialized) {
var parts = serialized.split("&");
this._name = decodeURIComponent(parts[0]);
this._state = parts[1];
this._timeSpentInPreviousIterations = parseInt(parts[2]);
this._currentIterationStartTime = parseInt(parts[3]);
this._startDate = parseInt(parts[4]);
this._endDate = parseInt(parts[5]);
this._taskid = parseInt(parts[6]);
this._description = parseInt(parts[7]);
}
}
/* ===== Tasks ===== */
/* The Tasks class represents a list of tasks. */
/* Creates a new Tasks object. */
function Tasks() {
this._tasks = [];
this._onAdd = null;
this._onRemove = null;
}
Tasks.prototype = {
/*
* Sets the "onAdd" event handler. The "onAdd" event is fired when a task
* is added to the list.
*/
setOnAdd: function(onAdd) {
this._onAdd = onAdd;
},
/*
* Sets the "onRemove" event handler. The "onRemove" event is fired when a
* task is removed from the list.
*/
setOnRemove: function(onRemove) {
this._onRemove = onRemove;
},
/* Returns the length of the task list. */
length: function() {
return this._tasks.length
},
/*
* Returns index-th task in the list, or "undefined" if the index is out of
* bounds.
*/
get: function(index) {
return this._tasks[index];
},
/*
* Calls the callback function for each task in the list. The function is
* called with three parameters - the task, its index and the task list
* object. This is modeled after "Array.forEach" in JavaScript 1.6.
*/
forEach: function(callback) {
for (var i = 0; i < this._tasks.length; i++) {
callback(this._tasks[i], i, this);
}
},
/* Calls the "onAdd" event handler if set. */
_callOnAdd: function(task) {
if (typeof this._onAdd == "function") {
this._onAdd(task);
}
},
/* Adds a new task to the end of the list. */
add: function(task) {
this._tasks.push(task);
this._callOnAdd(task);
},
/* Calls the "onRemove" event handler if set. */
_callOnRemove: function(index) {
if (typeof this._onRemove == "function") {
this._onRemove(index);
}
},
/*
* Removes index-th task from the list. Does not do anything if the index
* is out of bounds.
*/
remove: function(index) {
this._callOnRemove(index);
this._tasks.splice(index, 1);
},
/* Serializes the list of tasks into a string. */
serialize: function() {
var serializedTasks = [];
this.forEach(function(task) {
serializedTasks.push(task.serialize());
});
return serializedTasks.join("|");
},
/* Deserializes the list of tasks from a string. */
deserialize: function(serialized) {
/*
* Repeatedly use "remove" so the "onRemove" event is triggered. Do the same
* with the "add" method below.
*/
while (this._tasks.length > 0) {
this.remove(0);
}
var serializedTasks = serialized.split("|");
for (var i = 0; i < serializedTasks.length; i++) {
var task = new Task(0 ,"", "");
task.deserialize(serializedTasks[i]);
this.add(task);
}
}
}
/* ===== Extensions ===== */
/*
* Pads this string with another string on the left until the resulting string
* has specified length. If the padding string has more than one character, the
* resulting string may be longer than desired (the padding string is not
* truncated and it is only prepended as a whole). Bad API, I know, but it's
* good enough for me.
*/
String.prototype.pad = function(length, padding) {
var result = this;
while (result.length < length) {
result = padding + result;
}
return result;
}
/* ===== Task List + DOM Storage ===== */
/* The list of tasks. */
var tasks = new Tasks();
/* The last value of the serialized task list string. */
var lastSerializedTasksString;
/*
* The key under which the serialized task list string is stored in the DOM
* Storage.
*/
var TASKS_DOM_STORAGE_KEY = "timerTasks";
/*
* Returns DOM Storage used by the application, or "null" if the browser does
* not support DOM Storage.
*/
function getStorage() {
/*
* HTML 5 says that the persistent storage is available in the
* "window.localStorage" attribute, however Firefox implements older version
* of the proposal, which uses "window.globalStorage" indexed by the domain
* name. We deal with this situation here as gracefully as possible (i.e.
* without concrete browser detection and with forward compatibility).
*
* For more information, see:
*
* http://www.whatwg.org/specs/web-apps/current-work/#storage
* https://developer.mozilla.org/En/DOM/Storage
*/
if (window.localStorage !== undefined) {
return window.localStorage;
} else if (window.globalStorage !== undefined) {
return window.globalStorage[location.hostname];
} else {
return null;
}
}
/*
* Saves the task list into a DOM Storage. Also updates the value of the
* "lastSerializedTasksString" variable.
*/
function saveTasks() {
var serializedTasksString = tasks.serialize();
getStorage()[TASKS_DOM_STORAGE_KEY] = serializedTasksString;
lastSerializedTasksString = serializedTasksString;
}
/*
* Loads the serialized task list string from the DOM Storage. Returns
* "undefined" if the storage does not contain the string (this happens when
* running the application for the first time).
*/
function loadSerializedTasksString() {
var storedValue = getStorage()[TASKS_DOM_STORAGE_KEY];
/*
* The spec says "null" should be returned when the key is not found, but some
* browsers return "undefined" instead. Maybe it was in some earlier version
* of the spec (I didn't bother to check).
*/
if (storedValue !== null && storedValue !== undefined && storedValue.length > 0) {
/*
* The values retrieved from "globalStorage" use one more level of
* indirection.
*/
return (window.localStorage === undefined) ? storedValue.value : storedValue;
} else {
return undefined;
}
}
/*
* Loads the task list from the DOM Storage. Also updates the value of the
* "lastSerializedTasksString" variable.
*/
function loadTasks() {
var serializedTasksString = loadSerializedTasksString();
if (serializedTasksString !== undefined) {
tasks.deserialize(serializedTasksString);
lastSerializedTasksString = serializedTasksString;
}
}
/*
* Was the task list changed outside of the application? Detects the change
* by comparing the current serialized task list string in the DOM Storage
* with a kept old value.
*/
function tasksHaveChangedOutsideApplication() {
var serializedTasksString = loadSerializedTasksString();
if (serializedTasksString != lastSerializedTasksString) {
return true
}
return false;
}
/* ===== View ===== */
/* Some time constants. */
var MILISECONDS_IN_SECOND = 1000;
var MILISECONDS_IN_MINUTE = 60 * MILISECONDS_IN_SECOND;
var MINUTES_IN_HOUR = 60;
/* Formats the time in the H:MM format. */
function formatTime(time) {
var timeInMinutes = time / MILISECONDS_IN_MINUTE;
var hours = Math.floor(timeInMinutes / MINUTES_IN_HOUR);
var minutes = Math.floor(timeInMinutes - hours * MINUTES_IN_HOUR);
return hours + ":" + String(minutes).pad(2, "0");
}
/*
* Computes the URL of the image in the start/stop link according to the task
* state.
*/
function computeStartStopLinkImageUrl(state) {
switch (state) {
case Task.State.STOPPED:
return "fa-play";
case Task.State.RUNNING:
return "fa-stop";
default:
throw "Invalid task state."
}
}
/*
* Builds the HTML element of the row in the task table corresponding to the
* specified task and index.
*/
function buildTaskRow(task, index) {
var result = $("<tr />");
var startStopLink = $(
"<a href='#' class='start-stop-link' title='Start/stop'>"
+ "<i class='fa " + computeStartStopLinkImageUrl(task.getState()) + "'></i>"
);
startStopLink.click(function() {
switch (task.getState()) {
case Task.State.STOPPED:
task.start();
break;
case Task.State.RUNNING:
task.stop();
break;
default:
throw "Invalid task state."
}
saveTasks();
return false;
});
var resetLink = $(
"<a href='#' title='Reset'>"
+ "<i class='fa fa-undo'></i>"
);
resetLink.click(function() {
task.reset();
saveTasks();
return false;
});
var deleteLink = $(
"<a href='#' title='Delete' class='deletetask'>"
+ "<i class='fa fa-trash-alt' class='deletetask'></i>"
);
deleteLink.click(function() {
if (confirm("Do you really want to delete task \"" + task.getName() + "\"?")) {
tasks.remove(index);
saveTasks();
}
return false;
});
var saveLink = $(
"<a href='#' title='Save' class='savetask'>"
+ "<i class='fa fa-download' class='savetask'></i>"
);
saveLink.click(function() {
tasks.remove(index);
saveTasks();
task.save(task);
return false;
});
result
.addClass("state-" + task.getState())
.append($("<td class='task-name' />").text(task.getName()))
.append($("<td class='task-description' />").text(task.getDescription()))
.append($("<td class='task-time' />").text(formatTime(task.getTimeSpent())))
.append($("<td class='task-actions' />")
.append(startStopLink)
.append("&nbsp;&nbsp;")
.append(resetLink)
.append("&nbsp;&nbsp;")
.append(saveLink)
.append("&nbsp;&nbsp;&nbsp;&nbsp;")
.append(deleteLink)
);
return result;
}
/* Finds row with the specified index in the task table. */
function findRowWithIndex(index) {
return $("#task-table").find("tr").slice(1).eq(index);
}
/*
* Updates the row in the task table corresponding to a task according to
* its state.
*/
function updateTaskRow(row, task) {
if (task.isStopped()) {
row.removeClass("state-running");
row.addClass("state-stopped");
} else if (task.isRunning()) {
row.removeClass("state-stopped");
row.addClass("state-running");
}
row.find(".task-time").text(formatTime(task.getTimeSpent()))
row.find(".start-stop-link i").removeClass();
row.find(".start-stop-link i").addClass("fa "+computeStartStopLinkImageUrl(task.getState()));
}
/* ===== Initialization ===== */
/* Initializes event handlers on the task list. */
function initializeTasksEventHandlers() {
tasks.setOnAdd(function(task) {
var row = buildTaskRow(task, tasks.length() - 1);
$("#task-table").append(row);
task.setOnChange(function() {
updateTaskRow(row, task);
});
});
tasks.setOnRemove(function(index) {
findRowWithIndex(index).remove();
if (tasks.length() == 1 ) {
$( "#task-table" ).css({"display":"none"});
}
});
}
/* Initializes GUI event handlers. */
function initializeGuiEventHandlers() {
$( "#addtimer" ).click(function() {
displayTaskAdd();
});
$( "#timer-desc" ).keypress(function( event ) {
if ( event.which == 13 ) {
event.preventDefault();
displayTaskAdd();
}
});
}
function displayTaskAdd() {
$( "#task-table" ).css({"display":"inline"});
var taskName = $("#timer-task option:selected").text();
var taskId = $("#timer-task option:selected").val();
var taskDesc = $("#timer-desc").val();
var task = new Task(taskId, taskName, taskDesc);
tasks.add(task);
saveTasks();
$("#timer-desc").val("");
}
/*
* Initializes a timer used to update task times and detect changes in the
* data stored in the DOM Storage.
*/
function initializeTimer() {
setInterval(function() {
tasks.forEach(function(task, index) {
updateTaskRow(findRowWithIndex(index), task);
});
if (tasksHaveChangedOutsideApplication()) {
loadTasks();
}
}, 10 * MILISECONDS_IN_SECOND);
}
/* Initializes the application. */
$(document).ready(function(){
try {
if (!getStorage()) {
alert("Timer requires a browser with DOM Storage support, such as Firefox 3+ or Safari 4+.");
return;
}
} catch (e) {
alert("Timer does not work with file: URLs in Firefox.");
return;
}
initializeTasksEventHandlers();
loadTasks();
initializeGuiEventHandlers();
initializeTimer();
});

View File

@@ -33,6 +33,7 @@
"symfony/profiler-pack": "^1.0", "symfony/profiler-pack": "^1.0",
"symfony/security-bundle": "4.4.*", "symfony/security-bundle": "4.4.*",
"symfony/serializer-pack": "*", "symfony/serializer-pack": "*",
"symfony/swiftmailer-bundle": "^3.4",
"symfony/translation": "4.4.*", "symfony/translation": "4.4.*",
"symfony/twig-pack": "*", "symfony/twig-pack": "*",
"symfony/validator": "4.4.*", "symfony/validator": "4.4.*",

View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "24b27b5f4efd6efcabe5c89e1e1ac51b", "content-hash": "1be2a2e3a398eb23cd3c3e26bc75c090",
"packages": [ "packages": [
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
@@ -2557,6 +2557,68 @@
], ],
"time": "2019-12-27T08:57:19+00:00" "time": "2019-12-27T08:57:19+00:00"
}, },
{
"name": "swiftmailer/swiftmailer",
"version": "v6.2.3",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
"reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
"shasum": ""
},
"require": {
"egulias/email-validator": "~2.0",
"php": ">=7.0.0",
"symfony/polyfill-iconv": "^1.0",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "^1.0"
},
"require-dev": {
"mockery/mockery": "~0.9.1",
"symfony/phpunit-bridge": "^3.4.19|^4.1.8"
},
"suggest": {
"ext-intl": "Needed to support internationalized email addresses",
"true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.2-dev"
}
},
"autoload": {
"files": [
"lib/swift_required.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Chris Corbyn"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Swiftmailer, free feature-rich PHP mailer",
"homepage": "https://swiftmailer.symfony.com",
"keywords": [
"email",
"mail",
"mailer"
],
"time": "2019-11-12T09:31:26+00:00"
},
{ {
"name": "symfony/apache-pack", "name": "symfony/apache-pack",
"version": "v1.0.1", "version": "v1.0.1",
@@ -5811,6 +5873,71 @@
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2020-01-04T13:00:46+00:00" "time": "2020-01-04T13:00:46+00:00"
}, },
{
"name": "symfony/swiftmailer-bundle",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/swiftmailer-bundle.git",
"reference": "553d2474288349faed873da8ab7c1551a00d26ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/553d2474288349faed873da8ab7c1551a00d26ae",
"reference": "553d2474288349faed873da8ab7c1551a00d26ae",
"shasum": ""
},
"require": {
"php": ">=7.1",
"swiftmailer/swiftmailer": "^6.1.3",
"symfony/config": "^4.3.8|^5.0",
"symfony/dependency-injection": "^4.3.8|^5.0",
"symfony/http-kernel": "^4.3.8|^5.0"
},
"conflict": {
"twig/twig": "<1.41|<2.10"
},
"require-dev": {
"symfony/console": "^4.3.8|^5.0",
"symfony/framework-bundle": "^4.3.8|^5.0",
"symfony/phpunit-bridge": "^4.3.8|^5.0",
"symfony/yaml": "^4.3.8|^5.0"
},
"suggest": {
"psr/log": "Allows logging"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Bundle\\SwiftmailerBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony SwiftmailerBundle",
"homepage": "http://symfony.com",
"time": "2019-11-14T16:18:31+00:00"
},
{ {
"name": "symfony/templating", "name": "symfony/templating",
"version": "v4.4.5", "version": "v4.4.5",

View File

@@ -19,4 +19,5 @@ return [
Tetranz\Select2EntityBundle\TetranzSelect2EntityBundle::class => ['all' => true], Tetranz\Select2EntityBundle\TetranzSelect2EntityBundle::class => ['all' => true],
Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true], Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true],
Knp\Bundle\SnappyBundle\KnpSnappyBundle::class => ['all' => true], Knp\Bundle\SnappyBundle\KnpSnappyBundle::class => ['all' => true],
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
]; ];

View File

@@ -0,0 +1,5 @@
swiftmailer:
url: '%env(MAILER_URL)%'
spool:
type: file
path: '%kernel.project_dir%/var/spoolmail'

View File

@@ -0,0 +1,5 @@
swiftmailer:
url: '%env(MAILER_URL)%'
spool:
type: file
path: '%kernel.project_dir%/var/spoolmail'

View File

@@ -343,6 +343,52 @@ app_validationholiday_activeholiday:
path: /validator/validateholiday/activeholiday path: /validator/validateholiday/activeholiday
defaults: { _controller: App\Controller\ValidationController:activeholiday } defaults: { _controller: App\Controller\ValidationController:activeholiday }
#== Validationtimer ====================================================================================================
app_validationtimer:
path: /validator/validationtimer
defaults: { _controller: App\Controller\ValidationController:validationtimer }
app_validationtimer_validate:
path: /validator/validatetimer
defaults: { _controller: App\Controller\ValidationController:validatetimer }
app_validationtimer_devalidate:
path: /validator/devalidatetimer
defaults: { _controller: App\Controller\ValidationController:devalidatetimer }
app_validationtimer_activetimer:
path: /validator/validateholiday/activetimer
defaults: { _controller: App\Controller\ValidationController:activetimer }
#== Timer ====================================================================================================
app_timer:
path: /timer
defaults: { _controller: App\Controller\TimerController:list }
app_timer_view:
path: /timer/event
defaults: { _controller: App\Controller\TimerController:view }
app_timer_load:
path: /timer/event/load
defaults: { _controller: App\Controller\TimerController:load }
app_timer_submit:
path: /timer/submit
defaults: { _controller: App\Controller\TimerController:submit }
app_timer_create:
path: /timer/create
defaults: { _controller: App\Controller\TimerController:create }
app_timer_update:
path: /timer/update/{id}
defaults: { _controller: App\Controller\TimerController:update }
app_timer_delete:
path: /timer/delete/{id}
defaults: { _controller: App\Controller\TimerController:delete }
#== Customer ====================================================================================================== #== Customer ======================================================================================================
app_customer_report: app_customer_report:
@@ -353,6 +399,24 @@ app_customer_planning:
path: /customer/planning/{key} path: /customer/planning/{key}
defaults: { _controller: App\Controller\ReportController:planning, access: 'customer' } defaults: { _controller: App\Controller\ReportController:planning, access: 'customer' }
#== Export ======================================================================================================
app_export_view:
path: /export
defaults: { _controller: App\Controller\ExportController:view }
app_export_penalty_additional:
path: /export/export_penalty_additional
defaults: { _controller: App\Controller\ExportController:export_penalty_additional }
export_project_weekly:
path: /export/export_project_weekly
defaults: { _controller: App\Controller\ExportController:export_project_weekly }
export_full_worked_days:
path: /export/export_full_worked_days
defaults: { _controller: App\Controller\ExportController:export_full_worked_days }
#== API =========================================================================================================== #== API ===========================================================================================================
app_api: app_api:
path: /api/{key} path: /api/{key}

View File

@@ -9,6 +9,9 @@ parameters:
appName: '%env(resolve:APP_NAME)%' appName: '%env(resolve:APP_NAME)%'
appEnv: '%env(resolve:APP_ENV)%' appEnv: '%env(resolve:APP_ENV)%'
appCron: '%env(resolve:APP_CRON)%' appCron: '%env(resolve:APP_CRON)%'
appMailmethod: '%env(resolve:MAILER_METHOD)%'
appMailnoreply: '%env(resolve:MAILER_NOREPLY)%'
appMailnotif: '%env(resolve:MAILER_DEFAULT_NOTIF)%'
casHost: '%env(resolve:CAS_HOST)%' casHost: '%env(resolve:CAS_HOST)%'
casPort: '%env(resolve:CAS_PORT)%' casPort: '%env(resolve:CAS_PORT)%'
casPath: '%env(resolve:CAS_PATH)%' casPath: '%env(resolve:CAS_PATH)%'
@@ -16,6 +19,8 @@ parameters:
casEmail: '%env(resolve:CAS_EMAIL)%' casEmail: '%env(resolve:CAS_EMAIL)%'
casLastname: '%env(resolve:CAS_LASTNAME)%' casLastname: '%env(resolve:CAS_LASTNAME)%'
casFirstname: '%env(resolve:CAS_FIRSTNAME)%' casFirstname: '%env(resolve:CAS_FIRSTNAME)%'
officeHourStart: '%env(resolve:OFFICE_HOUR_START)%'
officeHourEnd: '%env(resolve:OFFICE_HOUR_END)%'
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file
@@ -59,3 +64,12 @@ services:
tags: tags:
- { name: kernel.event_listener, event: kernel.request, method: onDomainParse } - { name: kernel.event_listener, event: kernel.request, method: onDomainParse }
app.sendmail.transport:
class: Swift_SendmailTransport
public: true
arguments: ['/usr/sbin/sendmail -t']
app.mail.service:
public: true
class: App\Service\mailService
arguments: ["@mailer", "@twig"]

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash
if [ ! -f /var/www/html/schedule/.key ]; then if [ ! -f /var/www/html/schedule/.key ]; then
cat /dev/urandom | tr -dc '_A-Za-z0-9' | head -c${1:-32} > /var/www/html/schedule/.key openssl rand -hex 32 > /var/www/html/schedule/.key
fi fi

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;
class SendMailCommand extends Command
{
private $container;
private $em;
private $output;
private $filesystem;
private $rootlog;
public function __construct(ContainerInterface $container,EntityManagerInterface $em)
{
parent::__construct();
$this->container = $container;
$this->em = $em;
}
protected function configure()
{
$this
->setName('app:sendMail')
->setDescription('Envoi des mails')
->setHelp('Envoi des mails')
->addArgument('message-limit', InputArgument::OPTIONAL, 'limit message Mail')
->addArgument('env', InputArgument::OPTIONAL, 'env Mail')
->addArgument('cronid', InputArgument::OPTIONAL, 'ID Cron Job')
->addArgument('lastchance', InputArgument::OPTIONAL, 'Lastchance to run the cron')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->output = $output;
$this->filesystem = new Filesystem();
$this->rootlog = $this->container->get('kernel')->getRootDir()."/../var/logs/";
$this->writelnred('');
$this->writelnred('== app:sendMail');
$this->writelnred('==========================================================================================================');
$appMailmethod=$this->container->getParameter("appMailmethod");
$command = $this->getApplication()->find("swiftmailer:spool:send");
$tbparameter["--message-limit"]=100;
$tbparameter["--env"]="prod";
$tbparameter["--time-limit"]=5;
if($appMailmethod=="sendmail") $tbparameter["--transport"]="app.sendmail.transport";
$parameter = new ArrayInput($tbparameter);
try{
$returnCode = $command->run($parameter, $output);
}
catch(\Exception $e) {
$this->writeln("");
$this->writelnred("Impossible d'envoyer des mails");
$this->writeln("");
return 0;
}
$this->writeln("");
return 1;
}
private function writelnred($string) {
$this->output->writeln('<fg=red>'.$string.'</>');
$this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
}
private function writeln($string) {
$this->output->writeln($string);
$this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
}
}

View File

@@ -136,8 +136,15 @@ class BreakdayController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);

View File

@@ -77,7 +77,6 @@ class CropController extends AbstractController
// Cacul de la largeur // Cacul de la largeur
protected function getWidth($image) { protected function getWidth($image) {
dump($image);
$size = getimagesize($image); $size = getimagesize($image);
$width = $size[0]; $width = $size[0];
return $width; return $width;

View File

@@ -146,8 +146,15 @@ class CustomerController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);

View File

@@ -17,6 +17,11 @@ class EventController extends AbstractController
private $route = "app_event"; private $route = "app_event";
private $render = "Event/"; private $render = "Event/";
private $entity = "App:Event"; private $entity = "App:Event";
private $notificator;
public function __construct(\App\Service\notificationService $notificator) {
$this->notificator = $notificator;
}
public function list(Request $request) public function list(Request $request)
{ {
@@ -40,12 +45,26 @@ class EventController extends AbstractController
// Evenements // Evenements
$iduser=$this->get("session")->get("iduser"); $iduser=$this->get("session")->get("iduser");
$user=$em->getRepository("App:User")->find($iduser);
if($iduser=="all") if($iduser=="all")
$events=$em->getRepository("App:Event")->findAll(); $events=$em->getRepository("App:Event")->findAll();
else { else {
$iduser=$this->get("session")->get("iduser"); $users= [];
$user=$em->getRepository("App:User")->find($iduser); $fictivuser = $em->getRepository("App:User")->findOneBy([
$events=$em->getRepository("App:Event")->findBy(["user"=>$user]); 'service' => $user->getService(),
'fictive' => true,
]);
array_push($users,$fictivuser);
$selectedusers = $this->get("session")->get("selectedusers");
if (isset($selectedusers) && sizeof($selectedusers)>1){
foreach($selectedusers as $user) {
array_push($users,$em->getRepository("App:User")->find($user));
}
$events=$em->getRepository("App:Event")->findBy(["user"=>$users]);
}else{
array_push($users,$em->getRepository("App:User")->find($iduser));
$events=$em->getRepository("App:Event")->findBy(["user"=>$users]);
}
} }
foreach($events as $event) { foreach($events as $event) {
@@ -94,6 +113,7 @@ class EventController extends AbstractController
$am = ($request->request->get('am')=="true"); $am = ($request->request->get('am')=="true");
$ap = ($request->request->get('ap')=="true"); $ap = ($request->request->get('ap')=="true");
$astreinte = ($request->request->get('astreinte')=="true"); $astreinte = ($request->request->get('astreinte')=="true");
$externaltrip = ($request->request->get('externaltrip')=="true");
$description = $request->request->get('description'); $description = $request->request->get('description');
$user = $em->getRepository("App:User")->find($iduser); $user = $em->getRepository("App:User")->find($iduser);
@@ -237,6 +257,7 @@ class EventController extends AbstractController
$event->setEnd($dateend); $event->setEnd($dateend);
$event->setDuration($duration); $event->setDuration($duration);
$event->setAllday($allday); $event->setAllday($allday);
$event->setExternalTrip($externaltrip);
$event->setDescription($description); $event->setDescription($description);
$event->setUser($user); $event->setUser($user);
$event->setTask($task); $event->setTask($task);
@@ -245,6 +266,12 @@ class EventController extends AbstractController
$em->persist($event); $em->persist($event);
$em->flush(); $em->flush();
if($task->getNature()->getIsvacation()){
$idevent=$event->getId();
$valid_url = $this->generateUrl('app_validationholiday');
$this->notificator->sendNotifAttenteValid("Congés en attente de validation", $iduser, $idevent, $valid_url);
}
$output=$this->formatEvent($event); $output=$this->formatEvent($event);
} }
@@ -260,7 +287,10 @@ class EventController extends AbstractController
$idevent = str_replace("A","",$request->request->get('idevent')); $idevent = str_replace("A","",$request->request->get('idevent'));
$iduser = $request->request->get('iduser'); $iduser = $request->request->get('iduser');
$idtask = $request->request->get('idtask'); $idtask = $request->request->get('idtask');
$am = ($request->request->get('am')=="true");
$ap = ($request->request->get('ap')=="true");
$fgastreinte = ($request->request->get('fgastreinte')=="true"); $fgastreinte = ($request->request->get('fgastreinte')=="true");
$externaltrip = ($request->request->get('externaltrip')=="true");
$description = $request->request->get('description'); $description = $request->request->get('description');
$user = $em->getRepository("App:User")->find($iduser); $user = $em->getRepository("App:User")->find($iduser);
@@ -316,8 +346,74 @@ class EventController extends AbstractController
return new Response(json_encode($output)); return new Response(json_encode($output));
} }
$datestart=$event->getStart();
$dateend =$event->getEnd();
$duration=$dateend->diff($datestart)->d;
if($am&&$ap) {
if ($duration >= 1) {
$dateend->SetTime(0,0,-1);
}
$datestart->SetTime(0,0,0);
$dateend->add(new \DateInterval('P1D'));
$dateend->SetTime(0,0,0);
$duration=$dateend->diff($datestart)->d;
$allday=true;
}
else {
$duration=$dateend->diff($datestart)->d;
if ($duration == 1) {
$dateend->SetTime(0,0,-1);
}
$duration=0.5;
$allday=false;
if($am) {
$datestart->SetTime(9,0,0);
$dateend->SetTime(12,0,0);
}
else {
$datestart->SetTime(13,0,0);
$dateend->SetTime(17,0,0);
}
}
// On regarde si une tache ne commence pas pendant une autre intervention ou qui se termine pendant une autre intervention ou qui a une intervention compris dans ses dates
$events = $em->createQueryBuilder('event')
->select('event')
->from('App:Event','event')
->Where('event.user=:user AND event.start<=:start AND event.end >:start')
->orWhere('event.user=:user AND event.start<:end AND event.end >=:end')
->orWhere('event.user=:user AND event.start>:start AND event.end <:end')
->setParameter('user',$iduser)
->setParameter('start',$datestart)
->setParameter('end',$dateend)
->getQuery()->getResult();
if($events) {
$tbevent=[];
foreach($events as $ev) {
if ($event->getId() != $ev->getId()) {
$tmp=[
"id" => $ev->getId(),
"task" => $ev->getTask()->getName(),
"start" => $ev->getStart(),
"end" => $ev->getEnd(),
];
array_push($tbevent,$tmp);
}
}
if (sizeof($tbevent)>0) {
$output=["return"=>"KO","error"=>"Cet intervant a déjà une tache à cette date","start"=>$datestart,"end"=>$dateend,"events"=>$tbevent];
return new Response(json_encode($output));
}
}
// Modification de l'évenement // Modification de l'évenement
$event->setStart($datestart);
$event->setEnd($dateend);
$event->setDescription($description); $event->setDescription($description);
$event->setDuration($duration);
$event->setAllday($allday);
$event->setExternalTrip($externaltrip);
$event->setUser($user); $event->setUser($user);
$event->setTask($task); $event->setTask($task);
@@ -326,7 +422,6 @@ class EventController extends AbstractController
$output=$this->formatEvent($event); $output=$this->formatEvent($event);
} }
return new Response(json_encode($output)); return new Response(json_encode($output));
} }
@@ -400,12 +495,16 @@ class EventController extends AbstractController
"borderColor" => $event->getTask()->getColor(), "borderColor" => $event->getTask()->getColor(),
"textColor" => "#ffffff", "textColor" => "#ffffff",
"allDay" => $event->getAllday(), "allDay" => $event->getAllday(),
"holiday" => $event->getTask()->getNature()->getIsvacation(),
"externaltrip" => ($event instanceof Penalty?false:$event->getExternalTrip()),
"editable" => $editable, "editable" => $editable,
"fictivuser" => $event->getUser()->getFictive(),
"durationEditable" => false, "durationEditable" => false,
"extendedProps" => [ "extendedProps" => [
"fulldescription" => ($event instanceof Penalty?"ASTREINTE\n":"").strtoupper($event->getTask()->getDisplayname())."\n\n".$event->getDescription(), "fulldescription" => ($event instanceof Penalty?"ASTREINTE\n":"").strtoupper($event->getTask()->getDisplayname())."\n\n".$event->getDescription(),
"description" => $event->getDescription(), "description" => $event->getDescription(),
"userid" => $event->getUser()->getId(), "userid" => $event->getUser()->getId(),
"username" => $event->getUser()->getUsername(),
"taskid" => $event->getTask()->getId(), "taskid" => $event->getTask()->getId(),
"avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$event->getUser()->getAvatar(), "avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$event->getUser()->getAvatar(),
"estimate" => $event->getTask()->getDuration($event->getEnd())." / ".$event->getTask()->getQuantity(), "estimate" => $event->getTask()->getDuration($event->getEnd())." / ".$event->getTask()->getQuantity(),
@@ -429,12 +528,15 @@ class EventController extends AbstractController
"borderColor" => "#cdcdcd", "borderColor" => "#cdcdcd",
"textColor" => "#ffffff", "textColor" => "#ffffff",
"allDay" => true, "allDay" => true,
"externaltrip" => false,
"holiday" => false,
"editable" => false, "editable" => false,
"durationEditable" => false, "durationEditable" => false,
"extendedProps" => [ "extendedProps" => [
"fulldescription" => "Jour Férié", "fulldescription" => "Jour Férié",
"description" => "Jour Férié", "description" => "Jour Férié",
"userid" => null, "userid" => null,
"username" => "",
"taskid" => null, "taskid" => null,
"avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$this->getUser()->getAvatar(), "avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$this->getUser()->getAvatar(),
"estimate" => "", "estimate" => "",

View File

@@ -0,0 +1,435 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityManagerInterface;
class ExportController extends AbstractController
{
private $data = "export";
private $route = "app_exportèview";
private $render = "Export/";
private $entity = "App:Export";
public function view(Request $request)
{
$em = $this->getDoctrine()->getManager();
$iduser = $this->get("session")->get("iduser");
$user = $em->getRepository("App:User")->find($iduser);
return $this->render($this->render.'list.html.twig',[
"useheader" => true,
"usesidebar" => true,
"user" => $user,
]);
}
public function export_full_worked_days(Request $request,$access=null): Response
{
$em = $this->getDoctrine()->getManager();
$nbmonth=$this->get("session")->get("nbmonth");
$iduser=$this->get("session")->get("iduser");
if($iduser=="all") {
$users=$em->getRepository("App:User")->findAll();
}
else {
$users=$em->getRepository("App:User")->findBy(["id"=>$iduser]);
}
$tbevents=[];
foreach($users as $user) {
if(in_array("ROLE_USER",$user->getRoles())) {
// Filtre par Service
if($this->get('session')->get('idservice')!="all") {
if($user->getService()->getId()!=$this->get('session')->get('idservice'))
continue;
}
$tmp=[
"id" => $user->getId(),
"user" => $user,
"events" => [],
];
// On formate le tableau de jour
$start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->modify('previous monday');
$start->setTime(0,0,0);
$end = new \Datetime('first day of this month');
$end->add(new \DateInterval('P1M'));
$end->modify('next monday');
$end->setTime(23,59,0);
while($start<$end) {
$idday=$start->format("Ymd");
$idmonth=$start->format("Ym");
$tmp["events"][$idday] = [
"date"=>clone $start,
"allday"=>false,
"descriptionday"=>"",
"am"=>false,
"descriptionam"=>"",
"ap"=>false,
"descriptionap"=>"",
"astreinte"=>false,
"descriptionastreinte"=>"",
];
$start->add(new \DateInterval('P1D'));
}
// On formate le tableau des event
$start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->modify('previous monday');
$start->setTime(0,0,0);
$end = new \Datetime('first day of this month');
$end->add(new \DateInterval('P1M'));
$end->modify('next monday');
$end->setTime(23,59,0);
$events = $em
->createQueryBuilder('event')
->select('event')
->from('App:Event','event')
->Where('event.user=:user AND event.start>=:start AND event.end <:end')
->setParameter('user',$user->getId())
->setParameter('start',$start)
->setParameter('end',$end)
->getQuery()->getResult();
foreach($events as $event) {
$idproject=$event->getTask()->getProject()->getId();
// Filtre par project
if($this->get('session')->get('idproject')!="all") {
if($idproject!=$this->get('session')->get('idproject'))
continue;
}
$isvac= $event->getTask()->getNature()->getIsvacation();
if($isvac) {continue;}
$st=clone $event->getStart();
while($st<$event->getEnd()) {
$idday=$st->format("Ymd");
if($event->getAllday()) {
$tmp["events"][$idday]["allday"]=true;
$tmp["events"][$idday]["descriptionday"]=strtoupper($event->getTask()->getDisplayname());
}
else {
// Matin ou après-midi ?
$time=$event->getStart()->format("H");
if($time==9) {
$tmp["events"][$idday]["am"]=true;
if(isset($tmp["events"][$idday]["ap"]) && $tmp["events"][$idday]["ap"]==true){$tmp["events"][$idday]["allday"]=true;}
$tmp["events"][$idday]["descriptionam"]=strtoupper($event->getTask()->getDisplayname());
}
else {
$tmp["events"][$idday]["ap"]=true;
if(isset($tmp["events"][$idday]["am"]) && $tmp["events"][$idday]["am"] ==true){$tmp["events"][$idday]["allday"]=true;}
$tmp["events"][$idday]["descriptionap"]=strtoupper($event->getTask()->getDisplayname());
}
}
$st->add(new \DateInterval('P1D'));
}
}
// On formate le tableau des astreintes
$start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->modify('previous monday');
$start->setTime(0,0,0);
$end = new \Datetime('first day of this month');
$end->add(new \DateInterval('P1M'));
$end->modify('next monday');
$end->setTime(23,59,0);
$penaltys = $em
->createQueryBuilder('penalty')
->select('penalty')
->from('App:Penalty','penalty')
->Where('penalty.user=:user AND penalty.start>=:start AND penalty.end <:end')
->setParameter('user',$user->getId())
->setParameter('start',$start)
->setParameter('end',$end)
->getQuery()->getResult();
foreach($penaltys as $penalty) {
$idproject=$penalty->getTask()->getProject()->getId();
// Filtre par project
if($this->get('session')->get('idproject')!="all") {
if($idproject!=$this->get('session')->get('idproject'))
continue;
}
$st=clone $penalty->getStart();
while($st<$penalty->getEnd()) {
$idday=$st->format("Ymd");
if($penalty->getAllday()) {
$tmp["events"][$idday]["astreinte"]=true;
$tmp["events"][$idday]["descriptionastreinte"]=strtoupper($penalty->getTask()->getDisplayname());
}
$st->add(new \DateInterval('P1D'));
}
}
array_push($tbevents,$tmp);
}
}
$csv = $this->renderView('Export/export_full_worked_days.csv.twig', ["users" => $tbevents]);
$response = new Response($csv);
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition', 'attachment; filename="export_full_worked_days.csv"');
return $response;
}
public function export_penalty_additional(Request $request,$access=null): Response
{
$em = $this->getDoctrine()->getManager();
$iduser = $this->get("session")->get("iduser");
$timers = $em->getRepository("App:Timer")->findBy(["user"=>$iduser]);
$tbtimers = [];
foreach ($timers as $timer) {
$isactive = $timer->getActivePenalty();
$isadditional = $timer->getAdditionalHour();
if ($isactive || $isadditional) {
$tbtimer["id"] = $timer->getId();
$tbtimer["taskname"] = $timer->getTask()->getDisplayname();
$tbtimer["user"] = $timer->getUser()->getUsername();
$tbtimer["start"] = $timer->getStart()->format("Y-m-d H:i");
$tbtimer["end"] = $timer->getEnd()->format("Y-m-d H:i");
$tbtimer["duration"] = $timer->getDuration();
$tbtimer["activepenalty"] = $timer->getActivePenalty();
$tbtimer["additionalhour"] = $timer->getAdditionalHour();
$tbtimer["description"] = $timer->getDescription();
array_push($tbtimers, $tbtimer);
}
}
$csv = $this->renderView('Export/export_penalty_additional.csv.twig', ["timers" => $tbtimers]);
$response = new Response($csv);
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition', 'attachment; filename="export_penalty_additional.csv"');
return $response;
}
public function export_project_weekly(Request $request,$access=null): Response {
$nbmonth=$this->get("session")->get("nbmonth");
$em = $this->getDoctrine()->getManager();
$projects=$em->getRepository("App:Project")->findAll();
//Construction du tableau des projets
$tbprojects=[];
foreach($projects as $project) {
// Filtre par Customer
if($access=="customer") {
if($project->getCustomer()->getKeypass()!=$key)
continue;
}
// Filtre par Service
if($this->get('session')->get('idservice')!="all") {
if($project->getService()->getId()!=$this->get('session')->get('idservice'))
continue;
}
// Filtre par project
if($this->get('session')->get('idproject')!="all") {
if($project->getId()!=$this->get('session')->get('idproject'))
continue;
}
// Ne prendre que les projets actif/inactif
if($this->get('session')->get('activeproject')!=$project->getActive())
continue;
$tbproject= [];
$tbproject["projectname"] = $project->getDisplayname();
$tbproject["name"] = $project->getname();
$tbproject["customer"] = $project->getCustomer()->getName();
// Somme event validé par semaine
$start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->modify('previous monday');
$start->setTime(0,0,0);
$endmonth = new \Datetime('first day of this month');
$endmonth->add(new \DateInterval('P1M'));
$endmonth->modify('next monday');
$endmonth->setTime(23,59,0);
$eventsbyweek = $em
->createQueryBuilder('event')
->select('event')
->from('App:Task','task')
->from('App:Event','event')
->Where('task.project=:project')
->andWhere('event.task=task')
->andWhere('event.end >=:start')
->andWhere('event.end <:end')
->andWhere('event.validate=:validate')
->setParameter('project',$project)
->setParameter('validate',true)
->setParameter('start',$start)
->setParameter('end',$endmonth)
->orderBy('event.start')
->getQuery()->getResult();
// foreach($eventsbyweek as $event) {
// if(!isset($tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")])){
// $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [
// "weeknumber" => $event->getStart()->format("W"),
// "cumul" => 0,
// ];
// }
// $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"]+$event->getDuration();
// }
// foreach($eventsbyweek as $event) {
// if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"])){
// $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [
// "weeknumber" => $event->getStart()->format("W"),
// "users" => [],
// ];
// }
// if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()])){
// $tbuser= [
// "id"=>$event->getUser()->getId(),
// "displayname"=>$event->getUser()->getDisplayname(),
// "cumul"=>0
// ];
// $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()] = $tbuser;
// }
// $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration();
// }
foreach($eventsbyweek as $event) {
if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"])){
$tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [
"weeknumber" => $event->getStart()->format("W"),
"tasks" => [],
];
}
if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()])){
$tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()] = [
"taskname" => $event->getTask()->getName(),
"users" => [],
];
}
if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()])){
$tbuser= [
"id"=>$event->getUser()->getId(),
"displayname"=>$event->getUser()->getDisplayname(),
"cumul"=>0
];
$tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()] = $tbuser;
}
$tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration();
}
// Somme astreintes validé par semaine
$start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->modify('previous monday');
$start->setTime(0,0,0);
$endmonth = new \Datetime('first day of this month');
$endmonth->add(new \DateInterval('P1M'));
$endmonth->modify('next monday');
$endmonth->setTime(23,59,0);
$penaltybyweek = $em
->createQueryBuilder('penalty')
->select('penalty')
->from('App:Task','task')
->from('App:Penalty','penalty')
->Where('task.project=:project')
->andWhere('penalty.task=task')
->andWhere('penalty.end >=:start')
->andWhere('penalty.end <:end')
->andWhere('penalty.validate=:validate')
->setParameter('project',$project)
->setParameter('validate',true)
->setParameter('start',$start)
->setParameter('end',$endmonth)
->orderBy('penalty.start')
->getQuery()->getResult();
// foreach($penaltybyweek as $penalty) {
// if(!isset($tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")])){
// $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [
// "weeknumber" => $penalty->getStart()->format("W"),
// "cumul" => 0,
// ];
// }
// $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"]+$penalty->getDuration();
// }
// foreach($penaltybyweek as $penalty) {
// if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"])){
// $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [
// "weeknumber" => $penalty->getStart()->format("W"),
// "users" => [],
// ];
// }
// if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()])){
// $tbuser= [
// "id"=>$penalty->getUser()->getId(),
// "displayname"=>$penalty->getUser()->getDisplayname(),
// "cumul"=>0
// ];
// $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()] = $tbuser;
// }
// $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"]+$penalty->getDuration();
// }
foreach($penaltybyweek as $penalty) {
if(!isset($tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"])){
$tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [
"weeknumber" => $penalty->getStart()->format("W"),
"tasks" => [],
];
}
if(!isset($tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()])){
$tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()] = [
"taskname" => $penalty->getTask()->getName(),
"users" => [],
];
}
if(!isset($tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()])){
$tbuser= [
"id"=>$penalty->getUser()->getId(),
"displayname"=>$penalty->getUser()->getDisplayname(),
"cumul"=>0
];
$tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()] = $tbuser;
}
$tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()]["cumul"] = $tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()]["cumul"]+$penalty->getDuration();
}
$tbprojects[$project->getId()]=$tbproject;
}
$csv = $this->renderView('Export/export_project_weekly.csv.twig', ["projects" => $tbprojects]);
$response = new Response($csv);
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition', 'attachment; filename="export_project_weekly.csv"');
return $response;
}
}

View File

@@ -36,7 +36,9 @@ class HomeController extends AbstractController
public function selectuser(Request $request) public function selectuser(Request $request)
{ {
$iduser = $request->request->get('iduser'); $iduser = $request->request->get('iduser');
$selectedusers = $request->request->get('selectedusers');
$this->get('session')->set('iduser',$iduser); $this->get('session')->set('iduser',$iduser);
$this->get('session')->set('selectedusers',$selectedusers);
$output=["return"=>"OK"]; $output=["return"=>"OK"];
return new Response(json_encode($output)); return new Response(json_encode($output));
} }

View File

@@ -146,8 +146,15 @@ class JobController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);

View File

@@ -129,8 +129,15 @@ class NatureController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);

View File

@@ -146,8 +146,15 @@ class OfferController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);

View File

@@ -150,8 +150,16 @@ class ProjectController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
$this->refreshsession(); $this->refreshsession();
// Retour à la liste // Retour à la liste

View File

@@ -6,6 +6,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse; use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
class ReportController extends AbstractController class ReportController extends AbstractController
{ {
@@ -15,7 +16,6 @@ class ReportController extends AbstractController
public function synthese(Request $request) public function synthese(Request $request)
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$nbmonth=$this->get("session")->get("nbmonth"); $nbmonth=$this->get("session")->get("nbmonth");
$iduser=$this->get("session")->get("iduser"); $iduser=$this->get("session")->get("iduser");
@@ -122,6 +122,7 @@ class ReportController extends AbstractController
} }
} }
// On formate le tableau des astreintes // On formate le tableau des astreintes
$start=new \Datetime('first day of this month'); $start=new \Datetime('first day of this month');
$start->modify('last Monday'); $start->modify('last Monday');
@@ -205,6 +206,7 @@ class ReportController extends AbstractController
); );
} }
else { else {
return $this->render('Report/synthese.html.twig',[ return $this->render('Report/synthese.html.twig',[
"useheader" => true, "useheader" => true,
"usesidebar" => ($this->getUser()), "usesidebar" => ($this->getUser()),
@@ -513,11 +515,13 @@ class ReportController extends AbstractController
"beforeastreinte"=>[], "beforeastreinte"=>[],
"months"=>[], "months"=>[],
"offers"=>[], "offers"=>[],
"weeks"=>[],
]; ];
// Somme event validé avant la date // Somme event validé avant la date
$end=new \Datetime('first day of this month'); $end=new \Datetime('first day of this month');
$end->sub(new \DateInterval('P'.$nbmonth.'M')); $end->sub(new \DateInterval('P'.$nbmonth.'M'));
$end->setTime(23,59,0);
$events = $em $events = $em
->createQueryBuilder('event') ->createQueryBuilder('event')
->select('event') ->select('event')
@@ -533,19 +537,20 @@ class ReportController extends AbstractController
->orderBy('event.start') ->orderBy('event.start')
->getQuery()->getResult(); ->getQuery()->getResult();
foreach($events as $event) { foreach($events as $event) {
if(!isset($tbproject["before"][$event->getStart()->format("Ym")])) { if(!isset($tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")])) {
$tbproject["before"][$event->getStart()->format("Ym")] = [ $tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")] = [
"idmonth" => $event->getStart()->format("Ym"), "idmonth" => $event->getStart()->format("Ym"),
"monthlabel"=>$event->getStart()->format("m/Y"), "monthlabel"=>$event->getStart()->format("m/Y"),
"duration" => 0, "duration" => 0,
]; ];
} }
$tbproject["before"][$event->getStart()->format("Ym")]["duration"]=$tbproject["before"][$event->getStart()->format("Ym")]["duration"]+$event->getDuration(); $tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")]["duration"]=$tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")]["duration"]+$event->getDuration();
} }
// Somme astreinte validé avant la date // Somme astreinte validé avant la date
$end=new \Datetime('first day of this month'); $end=new \Datetime('first day of this month');
$end->sub(new \DateInterval('P'.$nbmonth.'M')); $end->sub(new \DateInterval('P'.$nbmonth.'M'));
$end->setTime(23,59,0);
$penaltys = $em $penaltys = $em
->createQueryBuilder('penalty') ->createQueryBuilder('penalty')
->select('penalty') ->select('penalty')
@@ -561,17 +566,150 @@ class ReportController extends AbstractController
->orderBy('penalty.start') ->orderBy('penalty.start')
->getQuery()->getResult(); ->getQuery()->getResult();
foreach($penaltys as $penalty) { foreach($penaltys as $penalty) {
if(!isset($tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")])) { if(!isset($tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")])) {
$tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")] = [ $tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")] = [
"idmonth" => $penalty->getStart()->format("Ym"), "idmonth" => $penalty->getStart()->format("Ym"),
"monthlabel"=>$penalty->getStart()->format("m/Y"), "monthlabel"=>$penalty->getStart()->format("m/Y"),
"duration" => 0, "duration" => 0,
]; ];
} }
$tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")]["duration"]=$tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")]["duration"]+$penalty->getDuration(); $tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")]["duration"]=$tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")]["duration"]+$penalty->getDuration();
} }
// Recap des propositions // Somme event validé par semaine
$start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->modify('previous monday');
$start->setTime(0,0,0);
$endmonth = new \Datetime('first day of this month');
$endmonth->add(new \DateInterval('P1M'));
$endmonth->modify('next monday');
$end->setTime(23,59,0);
$eventsbyweek = $em
->createQueryBuilder('event')
->select('event')
->from('App:Task','task')
->from('App:Event','event')
->Where('task.project=:project')
->andWhere('event.task=task')
->andWhere('event.end >=:start')
->andWhere('event.end <:end')
->andWhere('event.validate=:validate')
->setParameter('project',$project)
->setParameter('validate',true)
->setParameter('start',$start)
->setParameter('end',$endmonth)
->orderBy('event.start')
->getQuery()->getResult();
foreach($eventsbyweek as $event) {
if(!isset($tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")])){
$tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [
"weeknumber" => $event->getStart()->format("W"),
"cumul" => 0,
];
}
$tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"]+$event->getDuration();
}
// foreach($eventsbyweek as $event) {
// if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"])){
// $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [
// "weeknumber" => $event->getStart()->format("W"),
// "users" => [],
// ];
// }
// if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()])){
// $tbuser= [
// "id"=>$event->getUser()->getId(),
// "displayname"=>$event->getUser()->getDisplayname(),
// "cumul"=>0
// ];
// $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()] = $tbuser;
// }
// $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration();
// }
// foreach($eventsbyweek as $event) {
// if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"])){
// $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [
// "weeknumber" => $event->getStart()->format("W"),
// "tasks" => [],
// ];
// }
// if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()])){
// $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()] = [
// "taskname" => $event->getTask()->getName(),
// "users" => [],
// ];
// }
// if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()])){
// $tbuser= [
// "id"=>$event->getUser()->getId(),
// "displayname"=>$event->getUser()->getDisplayname(),
// "cumul"=>0
// ];
// $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()] = $tbuser;
// }
// $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration();
// }
// Somme astreintes validé par semaine
$start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->modify('previous monday');
$start->setTime(0,0,0);
$endmonth = new \Datetime('first day of this month');
$endmonth->add(new \DateInterval('P1M'));
$endmonth->modify('next monday');
$end->setTime(23,59,0);
$penaltybyweek = $em
->createQueryBuilder('penalty')
->select('penalty')
->from('App:Task','task')
->from('App:Penalty','penalty')
->Where('task.project=:project')
->andWhere('penalty.task=task')
->andWhere('penalty.end >=:start')
->andWhere('penalty.end <:end')
->andWhere('penalty.validate=:validate')
->setParameter('project',$project)
->setParameter('validate',true)
->setParameter('start',$start)
->setParameter('end',$endmonth)
->orderBy('penalty.start')
->getQuery()->getResult();
foreach($penaltybyweek as $penalty) {
if(!isset($tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")])){
$tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [
"weeknumber" => $penalty->getStart()->format("W"),
"cumul" => 0,
];
}
$tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"]+$penalty->getDuration();
}
foreach($penaltybyweek as $penaltybyweek) {
if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"])){
$tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [
"weeknumber" => $penalty->getStart()->format("W"),
"users" => [],
];
}
if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()])){
$tbuser= [
"id"=>$penalty->getUser()->getId(),
"displayname"=>$penalty->getUser()->getDisplayname(),
"cumul"=>0
];
$tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()] = $tbuser;
}
$tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"]+$penalty->getDuration();
}
// Recap des Commandes
$offers=$em->getRepository("App:Offer")->findBy(["project"=>$project->getId()]); $offers=$em->getRepository("App:Offer")->findBy(["project"=>$project->getId()]);
foreach($offers as $offer) { foreach($offers as $offer) {
$tbproject["offers"][$offer->getId()] = [ $tbproject["offers"][$offer->getId()] = [
@@ -584,9 +722,11 @@ class ReportController extends AbstractController
// Formater les mois // Formater les mois
$start=new \Datetime('first day of this month'); $start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M')); $start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->setTime(0,0,0);
$end=new \Datetime('first day of this month'); $end=new \Datetime('first day of this month');
$end->add(new \DateInterval('P'.$nbmonth.'M')); $end->add(new \DateInterval('P'.$nbmonth.'M'));
$end->sub(new \DateInterval('P1D')); $end->sub(new \DateInterval('P1D'));
$end->setTime(23,59,0);
while($start<$end) { while($start<$end) {
$tbproject["months"][$start->format("Ym")]=[ $tbproject["months"][$start->format("Ym")]=[
"monthid"=> $start->format("Ym"), "monthid"=> $start->format("Ym"),
@@ -670,13 +810,14 @@ class ReportController extends AbstractController
} }
$tbprojects[$project->getId()]=$tbproject; $tbprojects[$project->getId()]=$tbproject;
} }
// Formater les utilisateurs // Formater les utilisateurs
$start=new \Datetime('first day of this month'); $start=new \Datetime('first day of this month');
$start->sub(new \DateInterval('P'.$nbmonth.'M')); $start->sub(new \DateInterval('P'.$nbmonth.'M'));
$start->setTime(0,0,0);
$end=new \Datetime('first day of this month'); $end=new \Datetime('first day of this month');
$end->add(new \DateInterval('P'.$nbmonth.'M')); $end->add(new \DateInterval('P'.$nbmonth.'M'));
$end->sub(new \DateInterval('P1D')); $end->sub(new \DateInterval('P1D'));
$end->setTime(23,59,0);
foreach($users as $user) { foreach($users as $user) {
$tbevents = $this->getEventuser($user,$start,$end,true); $tbevents = $this->getEventuser($user,$start,$end,true);
@@ -695,7 +836,6 @@ class ReportController extends AbstractController
} }
} }
} }
// Cumule // Cumule
foreach($tbprojects as $project) { foreach($tbprojects as $project) {
foreach($project["months"] as $month) { foreach($project["months"] as $month) {
@@ -761,7 +901,6 @@ class ReportController extends AbstractController
private function getEventuser($user,$start,$end,$onlyvalidate) { private function getEventuser($user,$start,$end,$onlyvalidate) {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$tbevents=[]; $tbevents=[];
// Récupération de event // Récupération de event
$qb = $em $qb = $em
->createQueryBuilder('event') ->createQueryBuilder('event')
@@ -954,9 +1093,9 @@ class ReportController extends AbstractController
"usesidebar" => ($this->getUser()), "usesidebar" => ($this->getUser()),
"users" => $tbevents "users" => $tbevents
]); ]);
}
}
}
}
public function activeholiday() { public function activeholiday() {
$this->get('session')->set('activeholiday',!$this->get('session')->get('activeholiday')); $this->get('session')->set('activeholiday',!$this->get('session')->get('activeholiday'));
@@ -988,3 +1127,4 @@ class ReportController extends AbstractController
} }
} }
} }

View File

@@ -97,7 +97,7 @@ class SecurityController extends AbstractController
$user->setPassword("CASPWD-".$username); $user->setPassword("CASPWD-".$username);
$user->setSalt("CASPWD-".$username); $user->setSalt("CASPWD-".$username);
$user->setRoles(["ROLE_USER"]); $user->setRole("ROLE_USER");
$em->persist($user); $em->persist($user);
$em->flush(); $em->flush();
@@ -132,26 +132,14 @@ class SecurityController extends AbstractController
public function logout() { public function logout() {
$auth_mode=$this->getParameter("appAuth");
switch($auth_mode) {
case "MYSQL":
return $this->logoutMYSQL();
break;
case "CAS":
return $this->logoutCAS();
break;
}
}
public function logoutMYSQL() {
$this->get('security.token_storage')->setToken(null); $this->get('security.token_storage')->setToken(null);
$this->get('session')->invalidate(); $this->get('session')->invalidate();
return $this->redirect($this->generateUrl("app_home")); return $this->redirect($this->generateUrl("cnous_portal_homepage"));
} }
public function logoutcas() { public function logoutcas() {
// Init Client CAS // Init Client CAS
\phpCAS::setDebug('/var/www/html/schedule/var/log/cas.log'); \phpCAS::setDebug('/var/www/html/schedule/var/log/cas.log');
@@ -162,7 +150,5 @@ class SecurityController extends AbstractController
// Logout // Logout
$url=$this->generateUrl('app_home', array(), UrlGeneratorInterface::ABSOLUTE_URL); $url=$this->generateUrl('app_home', array(), UrlGeneratorInterface::ABSOLUTE_URL);
\phpCAS::logout(array("service"=>$url)); \phpCAS::logout(array("service"=>$url));
return true;
} }
} }

View File

@@ -9,6 +9,7 @@ use Symfony\Component\Form\FormError;
use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse; use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse;
use App\Entity\Service as Entity; use App\Entity\Service as Entity;
use App\Entity\User as Fictiv;
use App\Form\ServiceType as Form; use App\Form\ServiceType as Form;
class ServiceController extends AbstractController class ServiceController extends AbstractController
@@ -69,6 +70,16 @@ class ServiceController extends AbstractController
$em->persist($data); $em->persist($data);
$em->flush(); $em->flush();
$fictivuser = new Fictiv();
$fictivuser->setUsername($data->getName());
$fictivuser->setPassword(bin2hex(openssl_random_pseudo_bytes(4)));
$fictivuser->setLastname("Service");
$fictivuser->setFirstname($data->getName());
$fictivuser->setEmail($data->getName());
$fictivuser->setService($data->getId());
$fictivuser->setFictive(true);
$em->persist($fictivuser);
$em->flush();
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);
} }
@@ -146,9 +157,22 @@ class ServiceController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$fictivuser = $em->getRepository("App:User")->findOneBy([
'service' => $data->getService(),
'fictive' => true,
]);
$em->remove($fictivuser);
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);
} }

View File

@@ -146,8 +146,15 @@ class TaskController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste // Retour à la liste
return $this->redirectToRoute($this->route); return $this->redirectToRoute($this->route);

View File

@@ -0,0 +1,284 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\Timer as Entity;
use App\Form\TimerType as Form;
class TimerController extends AbstractController
{
private $data = "timer";
private $route = "app_timer";
private $render = "Timer/";
private $entity = "App:Timer";
public function list(Request $request)
{
$em = $this->getDoctrine()->getManager();
$iduser = $this->get("session")->get("iduser");
$user = $em->getRepository("App:User")->find($iduser);
$tasks = $em->getRepository("App:Task")->findAll();
$timers = $em->getRepository("App:Timer")->findBy(["user"=>$iduser]);
return $this->render($this->render.'list.html.twig',[
"useheader" => true,
"usesidebar" => true,
"user" => $user,
"tasks" => $tasks,
"timers" => $timers,
]);
}
public function view(Request $request)
{
$em = $this->getDoctrine()->getManager();
$iduser = $this->get("session")->get("iduser");
$user = $em->getRepository("App:User")->find($iduser);
$tasks = $em->getRepository("App:Task")->findAll();
$timers = $em->getRepository("App:Timer")->findBy(["user"=>$iduser]);
return $this->render($this->render.'list.cal.html.twig',[
"useheader" => true,
"usesidebar" => true,
"user" => $user,
"tasks" => $tasks,
"timers" => $timers,
]);
}
public function load(Request $request)
{
$em = $this->getDoctrine()->getManager();
$tbtimers=[];
// Evenements
$iduser=$this->get("session")->get("iduser");
if($iduser=="all")
$timers=$em->getRepository("App:Timer")->findAll();
else {
$iduser=$this->get("session")->get("iduser");
$user=$em->getRepository("App:User")->find($iduser);
$timers=$em->getRepository("App:Timer")->findBy(["user"=>$user]);
}
foreach($timers as $timer) {
$tmp=$this->formatTimer($timer);
array_push($tbtimers,$tmp);
}
// Retour
return new Response(json_encode($tbtimers));
}
public function create(Request $request)
{
// Initialisation de l'enregistrement
$em = $this->getDoctrine()->getManager();
$iduser = $this->get("session")->get("iduser");
$user = $em->getRepository("App:User")->find($iduser);
$taskid = $request->request->get('taskid');
$description = $request->request->get('description');
$task = $em->getRepository("App:Task")->find($taskid);
$start = \DateTime::createFromFormat('D M d Y H:i:s e+',$request->request->get('start'));
$end = \DateTime::createFromFormat('D M d Y H:i:s e+',$request->request->get('end'));
$duration = new \DateTime(date("H:i:s", ($request->request->get('duration')/1000)));
$duration->sub(new \DateInterval('PT1H'));
$activepenalty = $request->request->get('activepenalty');
$additionalhour= $request->request->get('additionalhour');
$officeworkstart = clone $start;
$officeworkend = clone $officeworkstart;
$officeworkstart->SetTime(9,0,0);
$officeworkend->SetTime(17,30,0);
if ($start < $officeworkstart || $end > $officeworkend) {
$timer->setAdditionalHour(true);
}else{
$timer->setAdditionalHour($additionalhour);
}
$timer = new Entity();
$timer->setUser($user);
$timer->setTask($task);
$timer->setStart($start);
$timer->setEnd($end);
$timer->setDuration($duration);
$timer->setDescription($description);
$timer->setActivePenalty($activepenalty);
$em->persist($timer);
$em->flush();
$output=["return"=>"OK"];
return new Response(json_encode($output));
}
public function submit(Request $request)
{
// Initialisation de l'enregistrement
$em = $this->getDoctrine()->getManager();
$data = new Entity();
$iduser = $this->get("session")->get("iduser");
$user = $em->getRepository("App:User")->find($iduser);
// Création du formulaire
$form = $this->createForm(Form::class,$data,array("mode"=>"submit"));
// Récupération des data du formulaire
$form->handleRequest($request);
// Sur erreur
$this->getErrorForm(null,$form,$request,$data,"submit");
// Sur validation
if ($form->get('submit')->isClicked() && $form->isValid()) {
$data = $form->getData();
$data->setUser($user);
$start = $data->getStart();
$end = $data->getEnd();
$additionalhour = $data->getAdditionalHour();
$officeworkstart = clone $start;
$officeworkend = clone $officeworkstart;
$uStart = explode(":",$this->getParameter('officeHourStart'));
$uEnd = explode(":",$this->getParameter('officeHourEnd'));
$officeworkstart->SetTime(intval($uStart[0]),intval($uStart[1]),0);
$officeworkend->SetTime(intval($uEnd[0]),intval($uEnd[1]),0);
if ($start < $officeworkstart || $end > $officeworkend) {
$data->setAdditionalHour(true);
}else{
$data->setAdditionalHour($additionalhour);
}
$em->persist($data);
$em->flush();
// Retour à la liste
return $this->redirectToRoute($this->route);
}
// Affichage du formulaire
return $this->render($this->render.'edit.html.twig', [
'useheader' => true,
'usesidebar' => true,
$this->data => $data,
'mode' => 'submit',
'form' => $form->createView()
]);
}
public function update($id,Request $request)
{
// Initialisation de l'enregistrement
$em = $this->getDoctrine()->getManager();
$data=$em->getRepository($this->entity)->find($id);
// Création du formulaire
$form = $this->createForm(Form::class,$data,array("mode"=>"update"));
// Récupération des data du formulaire
$form->handleRequest($request);
// Sur erreur
$this->getErrorForm(null,$form,$request,$data,"update");
// Sur validation
if ($form->get('submit')->isClicked() && $form->isValid()) {
$data = $form->getData();
$em->persist($data);
$em->flush();
// Retour à la liste
return $this->redirectToRoute($this->route);
}
return $this->render($this->render.'edit.html.twig', [
'useheader' => true,
'usesidebar' => true,
$this->data => $data,
'mode' => 'update',
'form' => $form->createView()
]);
}
public function delete($id, Request $request)
{
// Initialisation de l'enregistrement
$em = $this->getDoctrine()->getManager();
$data=$em->getRepository($this->entity)->find($id);
// Controle avant suppression
$error=false;
if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else {
try {
$em->remove($data);
$em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
// Retour à la liste
return $this->redirectToRoute($this->route);
}
}
protected function getErrorForm($id,$form,$request,$data,$mode) {
if ($form->get('submit')->isClicked()&&$mode=="delete") {
}
if ($form->get('submit')->isClicked() && $mode=="submit") {
}
if ($form->get('submit')->isClicked() && !$form->isValid()) {
$this->get('session')->getFlashBag()->clear();
$errors = $form->getErrors();
foreach( $errors as $error ) {
$request->getSession()->getFlashBag()->add("error", $error->getMessage());
}
}
}
public function formatTimer($timer) {
$tmp= [
"id"=> $timer->getId(),
"title" => $timer->getTask()->getDisplayname(),
"start" => $timer->getStart()->format("Y-m-d H:i"),
"end" => $timer->getEnd()->format("Y-m-d H:i"),
"backgroundColor" => $timer->getTask()->getColor(),
"borderColor" => $timer->getTask()->getColor(),
"textColor" => "#ffffff",
"allDay" => false,
"editable" => true,
"durationEditable" => false,
"extendedProps" => [
"fulldescription" => $timer->getTask()->getDisplayname()."\n\n".$timer->getDescription(),
"description" => $timer->getDescription(),
"userid" => $timer->getUser()->getId(),
"username" => $timer->getUser()->getUsername(),
"taskid" => $timer->getTask()->getId(),
"avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$timer->getUser()->getAvatar(),
"estimate" => $timer->getDuration()->format("H:i"),
"locked" => false,
"editable" => true,
"astreinte" => false
]
];
return $tmp;
}
}

View File

@@ -202,8 +202,16 @@ class UserController extends AbstractController
if($error) if($error)
return $this->redirectToRoute($this->route."_update",["id"=>$id]); return $this->redirectToRoute($this->route."_update",["id"=>$id]);
else { else {
try {
$em->remove($data); $em->remove($data);
$em->flush(); $em->flush();
}
catch(\Doctrine\DBAL\DBALException $e) {
// Création du formulaire
$this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement');
return $this->redirectToRoute($this->route."_update",["id"=>$id]);
}
$this->refreshsession(); $this->refreshsession();
// Retour à la liste // Retour à la liste

View File

@@ -10,7 +10,11 @@ use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse;
class ValidationController extends AbstractController class ValidationController extends AbstractController
{ {
private $knpSnappy; private $knpSnappy;
public function __construct(\Knp\Snappy\Pdf $knpSnappy) { $this->knpSnappy = $knpSnappy; } private $notificator;
public function __construct(\Knp\Snappy\Pdf $knpSnappy, \App\Service\notificationService $notificator) {
$this->knpSnappy = $knpSnappy;
$this->notificator = $notificator;
}
public function validation(Request $request) public function validation(Request $request)
{ {
@@ -510,6 +514,9 @@ class ValidationController extends AbstractController
$event->setValidateholiday(true); $event->setValidateholiday(true);
$em->persist($event); $em->persist($event);
$em->flush(); $em->flush();
$iduser=$this->get("session")->get("iduser");
$idevent=$event->getId();
$this->notificator->sendNotifValid("Congé validé", $iduser, $event);
} }
$output=[]; $output=[];
@@ -543,4 +550,126 @@ class ValidationController extends AbstractController
return $this->redirectToRoute("app_validationholiday"); return $this->redirectToRoute("app_validationholiday");
} }
public function validationtimer(Request $request)
{
$em = $this->getDoctrine()->getManager();
$iduser=$this->get("session")->get("iduser");
if($iduser=="all") {
$users=$em->getRepository("App:User")->findAll();
}
else {
$users=$em->getRepository("App:User")->findBy(["id"=>$iduser]);
}
$tbtimers=[];
foreach($users as $user) {
if(in_array("ROLE_USER",$user->getRoles())) {
// Filtre par Service
if($this->get('session')->get('idservice')!="all") {
if($user->getService()->getId()!=$this->get('session')->get('idservice'))
continue;
}
$tmp=[
"id" => $user->getId(),
"user" => $user,
"timers" => [],
"timerstodevalidate" => [],
];
// Timers à valider ou à dévalider
$timers = $em
->createQueryBuilder('timer')
->select('timer')
->from('App:Timer','timer')
->from('App:Task','task')
->Where('timer.user=:user')
->andWhere('timer.validate=:validate')
->andWhere('task=timer.task')
->setParameter('user',$user->getId())
->setParameter('validate',!($this->get("session")->get("activetimer")))
->getQuery()->getResult();
foreach($timers as $timer) {
$tbtimer = [
"id"=>$timer->getId(),
"taskname"=>$timer->getTask()->getDisplayname(),
"user"=>$timer->getUser()->getUsername(),
"start"=>$timer->getStart()->format("Y-m-d H:i"),
"end"=>$timer->getEnd()->format("Y-m-d H:i"),
"duration"=>$timer->getDuration(),
"activepenalty"=>$timer->getActivePenalty(),
"additionalhour"=>$timer->getAdditionalHour(),
"description"=>$timer->getDescription(),
"validate"=>$timer->getValidate(),
];
array_push($tmp["timers"],$tbtimer);
}
array_push($tbtimers,$tmp);
}
}
if($request->query->get('fgprint')) {
$render = $this->renderView('Validation/validationtimer.html.twig',[
"useheader" => true,
"usesidebar" => ($this->getUser()),
"users" => $tbtimers,
"fgprint" => $request->query->get('fgprint'),
]);
return new PdfResponse(
$this->knpSnappy->getOutputFromHtml($render),
'validationhoraires.pdf'
);
}
else {
return $this->render('Validation/validationtimer.html.twig',[
"useheader" => true,
"usesidebar" => ($this->getUser()),
"users" => $tbtimers
]);
}
}
public function validatetimer(Request $request){
$em = $this->getDoctrine()->getManager();
// Récupération des variables envoyées en post
$id = $request->request->get('id');
$timer=$em->getRepository("App:Timer")->find($id);
if($timer) {
$timer->setValidate(true);
$em->persist($timer);
$em->flush();
$iduser=$this->get("session")->get("iduser");
$idtimer=$timer->getId();
}
$output=[];
return new Response(json_encode($output));
}
public function devalidatetimer(Request $request){
$em = $this->getDoctrine()->getManager();
// Récupération des variables envoyées en post
$id = $request->request->get('id');
$timer=$em->getRepository("App:Timer")->find($id);
if($timer) {
$timer->setValidate(false);
$em->persist($timer);
$em->flush();
}
$output=[];
return new Response(json_encode($output));
}
public function activetimer() {
$this->get('session')->set('activetimer',!$this->get('session')->get('activetimer'));
return $this->redirectToRoute("app_validationtimer");
}
} }

View File

@@ -50,6 +50,12 @@ class Event
*/ */
private $allday; private $allday;
/**
* @ORM\Column(name="externaltrip", type="boolean")
*
*/
private $externaltrip;
/** /**
* @ORM\Column(name="validate", type="boolean") * @ORM\Column(name="validate", type="boolean")
* *
@@ -96,24 +102,24 @@ class Event
public function getStart(): ?\DateTimeInterface public function getStart(): ?\DateTimeInterface
{ {
return $this->start; return clone $this->start;
} }
public function setStart(\DateTimeInterface $start): self public function setStart(\DateTimeInterface $start): self
{ {
$this->start = $start; $this->start = clone $start;
return $this; return $this;
} }
public function getEnd(): ?\DateTimeInterface public function getEnd(): ?\DateTimeInterface
{ {
return $this->end; return clone $this->end;
} }
public function setEnd(\DateTimeInterface $end): self public function setEnd(\DateTimeInterface $end): self
{ {
$this->end = $end; $this->end = clone $end;
return $this; return $this;
} }
@@ -129,6 +135,17 @@ class Event
return $this; return $this;
} }
public function getExternalTrip(): ?bool
{
return $this->externaltrip;
}
public function setExternalTrip(bool $externaltrip): self
{
$this->externaltrip = $externaltrip;
return $this;
}
public function getValidate(): ?bool public function getValidate(): ?bool
{ {

View File

@@ -61,6 +61,11 @@ class Task
*/ */
private $events; private $events;
/**
* @ORM\OneToMany(targetEntity="Timer", mappedBy="task", cascade={"persist"}, orphanRemoval=false)
*/
private $timers;
/** /**
* @ORM\OneToMany(targetEntity="Penalty", mappedBy="task", cascade={"persist"}, orphanRemoval=false) * @ORM\OneToMany(targetEntity="Penalty", mappedBy="task", cascade={"persist"}, orphanRemoval=false)
*/ */
@@ -90,6 +95,7 @@ class Task
public function __construct() public function __construct()
{ {
$this->events = new ArrayCollection(); $this->events = new ArrayCollection();
$this->timers = new ArrayCollection();
$this->penaltys = new ArrayCollection(); $this->penaltys = new ArrayCollection();
} }
@@ -191,6 +197,24 @@ class Task
return $this; return $this;
} }
/**
* @return Collection|Timer[]
*/
public function getTimers(): Collection
{
return $this->tasks;
}
public function addTimer(Timer $timer): self
{
if (!$this->timers->contains($timer)) {
$this->timers[] = $timer;
$timer->setTask($this);
}
return $this;
}
public function removeEvent(Event $event): self public function removeEvent(Event $event): self
{ {
if ($this->events->contains($event)) { if ($this->events->contains($event)) {
@@ -235,4 +259,17 @@ class Task
return $this; return $this;
} }
public function removeTimer(Timer $timer): self
{
if ($this->timers->contains($timer)) {
$this->timers->removeElement($timer);
// set the owning side to null (unless already changed)
if ($timer->getTask() === $this) {
$timer->setTask(null);
}
}
return $this;
}
} }

View File

@@ -0,0 +1,197 @@
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Timer
*
* @ORM\Table(name="timer")
* @ORM\Entity(repositoryClass="App\Repository\TimerRepository")
*/
class Timer
{
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(name="start", type="datetime")
*
*/
private $start;
/**
* @ORM\Column(name="end", type="datetime")
*
*/
private $end;
/**
* @ORM\Column(name="duration", type="datetime")
*
*/
private $duration;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $description;
/**
* @ORM\ManyToOne(targetEntity="User")
*/
private $user;
/**
* @ORM\ManyToOne(targetEntity="Task", inversedBy="timers"))
*/
private $task;
/**
* @ORM\Column(name="activepenalty", type="boolean")
*
*/
private $activepenalty;
/**
* @ORM\Column(name="additionalhour", type="boolean")
*
*/
private $additionalhour;
/**
* @ORM\Column(name="validate", type="boolean")
*
*/
private $validate;
public function __construct()
{
$this->start = new \DateTime();
$this->end = new \DateTime();
$this->duration = new \DateTime();
$this->validate = false;
}
public function getId(): ?int
{
return $this->id;
}
public function getStart(): ?\DateTimeInterface
{
return $this->start;
}
public function setStart(\DateTimeInterface $start): self
{
$this->start = $start;
return $this;
}
public function getEnd(): ?\DateTimeInterface
{
return $this->end;
}
public function setEnd(\DateTimeInterface $end): self
{
$this->end = $end;
return $this;
}
public function getDuration(): ?\DateTimeInterface
{
return $this->duration;
}
public function setDuration(\DateTimeInterface $duration): self
{
$this->duration = $duration;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getTask(): ?Task
{
return $this->task;
}
public function setTask(?Task $task): self
{
$this->task = $task;
return $this;
}
public function getActivePenalty(): ?bool
{
return $this->activepenalty;
}
public function setActivePenalty(bool $activepenalty): self
{
$this->activepenalty = $activepenalty;
return $this;
}
public function getAdditionalHour(): ?bool
{
return $this->additionalhour;
}
public function setAdditionalHour(bool $additionalhour): self
{
$this->additionalhour = $additionalhour;
return $this;
}
public function getValidate(): ?bool
{
return $this->validate;
}
public function setValidate(bool $validate): self
{
$this->validate = $validate;
return $this;
}
}

View File

@@ -109,6 +109,11 @@ class User implements UserInterface, \Serializable
* @ORM\OneToMany(targetEntity="Userproject", mappedBy="user", cascade={"persist"}, orphanRemoval=true) * @ORM\OneToMany(targetEntity="Userproject", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
*/ */
private $userprojects; private $userprojects;
/**
* @ORM\Column(name="fictive", type="boolean")
*
*/
private $fictive;
public function __construct() public function __construct()
{ {
@@ -117,6 +122,7 @@ class User implements UserInterface, \Serializable
$this->penaltys = new ArrayCollection(); $this->penaltys = new ArrayCollection();
$this->jobs = new ArrayCollection(); $this->jobs = new ArrayCollection();
$this->userprojects = new ArrayCollection(); $this->userprojects = new ArrayCollection();
$this->fictive = false;
} }
public function getUsername(): ?string public function getUsername(): ?string
@@ -408,5 +414,17 @@ class User implements UserInterface, \Serializable
return $this; return $this;
} }
public function getFictive(): ?bool
{
return $this->fictive;
}
public function setFictive(bool $fictive): self
{
$this->fictive = $fictive;
return $this;
}
} }

View File

@@ -0,0 +1,110 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;
use Symfony\Component\Form\ChoiceList\ChoiceList;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use Tetranz\Select2EntityBundle\Form\Type\Select2EntityType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
class TimerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('submit',
SubmitType::class, [
"label" => "Valider",
"attr" => ["class" => "btn btn-success no-print"],
]
);
$builder->add('task',
EntityType::class, [
"label" => "Tâche",
"class" => "App:Task",
"choice_label" => function ($task) {
return $task->getDisplayname();},
"disabled" => false,
"required" => true,
"multiple" => false,
"placeholder" => "Selectionner une Tâche",
]
);
$builder->add('description',
TextType::class, [
"label" => "Description",
"required" => false
]
);
$builder->add("activepenalty",
ChoiceType::class,[
"label" => "Astreinte active",
"choices" => ["Non"=>false, "Oui"=>true]
]
);
$builder->add("additionalhour",
ChoiceType::class,[
"label" => "Heures supplémentaires",
"choices" => ["Non"=>false, "Oui"=>true]
]
);
$builder->add('start',
DateTimeType::class, [
"label" =>"Début",
"date_widget" => "single_text",
"time_widget" => "single_text",
"format" => "yyyy-MM-dd HH:mm",
]
);
$builder->add('end',
DateTimeType::class, [
"label" =>"Fin",
"date_widget" => "single_text",
"time_widget" => "single_text",
"format" => "yyyy-MM-dd HH:mm",
]
);
$builder->add('duration',
TimeType::class, [
"label" =>"Durée",
"widget" => "single_text",
]
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'App\Entity\Timer',
'mode' => 'string',
));
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace App\Service;
use Doctrine\ORM\EntityManagerInterface;
class notificationService
{
private $em;
protected $mailer;
protected $twig;
public function __construct(EntityManagerInterface $em, \Swift_Mailer $mailer, \Twig\Environment $twig)
{
$this->mailer = $mailer;
$this->twig = $twig;
$this->em = $em;
}
public function sendNotifAttenteValid($subject, $iduser, $idevent, $valid_url)
{
$template = $this->twig->load('Notif/attentevalidation.html.twig');
$user=$this->em->getRepository("App:User")->find($iduser);
$event=$this->em->getRepository("App:Event")->find($idevent);
$users=$this->em->getRepository("App:User")->findAll();
$tbemails=[];
foreach($users as $usr) {
if(in_array("ROLE_VALIDATOR",$usr->getRoles())) {
array_push($tbemails,$usr->getEmail());
}
// else{
// if(in_array("ROLE_MASTER",$usr->getRoles())) {
// if ($usr->getService() == $user->getService() ){
// $tmp=[
// "id"=>$usr->getId(),
// "email"=>$usr->getEmail()
// ];
// }
// array_push($tbemails,$tmp);
// }
// }
}
$parameters=[
'date' => new \DateTime(),
'username' => $user->getUsername(),
'start' => $event->getStart(),
'end' => $event->getEnd(),
'duration' => $event->getDuration(),
'valid_link' => $valid_url,
];
$bodyHtml = $template->renderBlock('body', $parameters);
$message = (new \Swift_Message())
->setFrom('schedule@cadoles.com')
->setSubject($user->getUsername() . " = " . $subject)
//TODO envoyer à tt les users master associé au service de l'utilisateur et l'ensemble des user validator
->setTo($tbemails)
->setBody($bodyHtml, 'text/html');
$response = $this->mailer->send($message);
return $response;
}
public function sendNotifValid($subject, $iduser, $idevent)
{
$template = $this->twig->load('Notif/validation.html.twig');
$user=$this->em->getRepository("App:User")->find($iduser);
$event=$this->em->getRepository("App:Event")->find($idevent);
$users=$this->em->getRepository("App:User")->findAll();
$tbemails=[];
foreach($users as $usr) {
if(in_array("ROLE_VALIDATOR",$usr->getRoles())) {
array_push($tbemails,$usr->getEmail());
}
}
$parameters=[
'date' => new \DateTime(),
'username' => $user->getUsername(),
'start' => $event->getStart(),
'end' => $event->getEnd(),
'duration' => $event->getDuration(),
];
$bodyHtml = $template->renderBlock('body', $parameters);
$message = (new \Swift_Message())
->setFrom('schedule@cadoles.com')
->setSubject($user->getUsername() . " = " . $subject)
->setTo($this->container->getParameter('appMailnotif'))
->setCc($tbemails)
->setBody($bodyHtml, 'text/html');
$response = $this->mailer->send($message);
return $response;
}
}

View File

@@ -69,6 +69,7 @@ class sessionListener {
$session->set('activeproject',true); $session->set('activeproject',true);
$session->set('activeoffer',true); $session->set('activeoffer',true);
$session->set('activeholiday',true); $session->set('activeholiday',true);
$session->set('activetimer',true);
$session->set('nbmonth',3); $session->set('nbmonth',3);
if($curentuser!="anon.") { if($curentuser!="anon.") {
@@ -120,6 +121,9 @@ class sessionListener {
array_push($tbservices,$tmp); array_push($tbservices,$tmp);
} }
$session->set('services',$tbservices); $session->set('services',$tbservices);
$selectedusers=[];
$session->set('selectedusers',$selectedusers);
} }
} }
} }

View File

@@ -21,23 +21,31 @@
.fc-title { .fc-title {
font-weight: bolder; font-weight: bolder;
font-size: 14px; font-size: 12px;
} }
.eventAvatar { .eventAvatar {
width: 40px; width: 20px;
margin: 0px 5px 0px 0px; margin: 0px 5px 0px 0px;
float: left; float: left;
} }
.eventInfo{ .eventInfo{
margin: -18px 5px 0px 0px; margin: -5px 5px 0px 0px;
clear: both;
}
.eventUser{
clear: both; clear: both;
} }
.eventEstimate { .eventEstimate {
margin: -3px 10px; margin: -3px 10px;
} }
.fictive{
height:10px!important;
background-color:#cdcdcd;
}
{% endblock %} {% endblock %}
{% block body %} {% block body %}
@@ -105,6 +113,12 @@
<label class="custom-control-label" for="astreinte">Astreinte</label> <label class="custom-control-label" for="astreinte">Astreinte</label>
</div> </div>
</div> </div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="externaltrip">
<label class="custom-control-label" for="externaltrip">Déplacement externe</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="description" class="control-label"> <label for="description" class="control-label">
@@ -153,7 +167,6 @@
{% endif %} {% endif %}
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label required" for="taskupdate"> <label class="control-label required" for="taskupdate">
Project<span class="mandatory">*</span> Project<span class="mandatory">*</span>
@@ -165,7 +178,25 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="amupdate">
<label class="custom-control-label" for="amupdate">Evènement sur la matinée</label>
</div>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="apupdate">
<label class="custom-control-label" for="apupdate">Evènement sur l'après-midi</label>
</div>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="externaltripupdate">
<label class="custom-control-label" for="externaltripupdate">Déplacement externe</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="description" class="control-label"> <label for="description" class="control-label">
Description Description
@@ -188,6 +219,7 @@
{% block localjavascript %} {% block localjavascript %}
$(document).ready(function() { $(document).ready(function() {
$("#modalsubmit #user").select2({ $("#modalsubmit #user").select2({
theme: 'bootstrap4', theme: 'bootstrap4',
@@ -215,12 +247,16 @@ $(document).ready(function() {
// Rendu d'un évenement // Rendu d'un évenement
function eventRender(info) { function eventRender(info) {
console.log(info.event.extendedProps);
// Récupération des divers élements du rendu event // Récupération des divers élements du rendu event
var content=$(info.el).children('.fc-content'); var content=$(info.el).children('.fc-content');
var title=$(content).children('.fc-title'); var title=$(content).children('.fc-title');
if(!info.event.extendedProps.fictivuser){
// Ajouter l'avatar // Ajouter l'avatar
content.prepend("<img src="+info.event.extendedProps.avatar+" class='eventAvatar'>"); content.prepend("<img src="+info.event.extendedProps.avatar+" class='eventAvatar'>");
content.append("<span class='eventUser float-left small'>"+info.event.extendedProps.username+"</span>");
var eventInfo=$(content).children('.eventUser');
// Ajout container // Ajout container
content.append("<span class='eventInfo float-right'></span>"); content.append("<span class='eventInfo float-right'></span>");
@@ -231,14 +267,30 @@ function eventRender(info) {
eventInfo.append("<i class='fa fa-lock float-right'></i>"); eventInfo.append("<i class='fa fa-lock float-right'></i>");
} }
if(info.event.extendedProps.externaltrip) {
eventInfo.append("<i class='fas fa-bed float-right'></i>");
}
if(info.event.extendedProps.holiday) {
eventInfo.append("<i class='fas fa-umbrella-beach float-right'></i>");
}
// Ajout estimation // Ajout estimation
eventInfo.append("<span class='eventEstimate float-right'>"+info.event.extendedProps.estimate+"</span>"); eventInfo.append("<span class='eventEstimate float-right small'>"+info.event.extendedProps.estimate+"</span>");
}else{
$(content).addClass('fictive');
$(content).children('.fc-title').remove();
}
// Description // Description
content.attr("data-placement","top");
content.attr("data-html",true);
content.attr("data-toggle","tooltip");
content.attr("title",info.event.extendedProps.fulldescription); content.attr("title",info.event.extendedProps.fulldescription);
content.tooltip()
} }
// Formulaire Création d'un évelement // Formulaire Création d'un événement
var allDay; var allDay;
function eventSelect(selectionInfo) { function eventSelect(selectionInfo) {
var start=moment(selectionInfo.start); var start=moment(selectionInfo.start);
@@ -284,6 +336,7 @@ function eventSelect(selectionInfo) {
$('#modalsubmit #end').val(end.format("YYYY-MM-DD")); $('#modalsubmit #end').val(end.format("YYYY-MM-DD"));
$('#modalsubmit #description').val(""); $('#modalsubmit #description').val("");
$('#modalsubmit #externaltrip').prop("checked",false);
$("#modalsubmit .alert").remove(); $("#modalsubmit .alert").remove();
@@ -291,24 +344,64 @@ function eventSelect(selectionInfo) {
$('#modalsubmit').modal(); $('#modalsubmit').modal();
} }
// Formulaire Modification d'un évelement // Formulaire Modification d'un événement
function eventClick(info) { function eventClick(info) {
if(info.event.extendedProps.editable) { if(info.event.extendedProps.editable) {
console.log(info.event.id); console.log(info.event);
var id=info.event.id; var id=info.event.id;
var description=info.event.extendedProps.description; var description=info.event.extendedProps.description;
var userid=info.event.extendedProps.userid; var userid=info.event.extendedProps.userid;
var taskid=info.event.extendedProps.taskid; var taskid=info.event.extendedProps.taskid;
var fgastreinte=info.event.extendedProps.astreinte; var fgastreinte=info.event.extendedProps.astreinte;
var eventallday = info.event.allDay;
var eventstart = info.event.start;
var eventend = info.event.end;
var externaltrip = info.event.extendedProps.externaltrip;
var holiday = info.event.extendedProps.holiday;
$('#userupdate').val(userid).trigger("change"); $('#userupdate').val(userid).trigger("change");
$('#taskupdate').val(taskid).trigger("change"); $('#taskupdate').val(taskid).trigger("change");
$('#modalupdate #idevent').val(id); $('#modalupdate #idevent').val(id);
$('#modalupdate #fgastreinte').val(fgastreinte); $('#modalupdate #fgastreinte').val(fgastreinte);
$('#modalupdate #description').val(description); $('#modalupdate #description').val(description);
if (holiday) {
$('#modalupdate #externaltripupdate').prop("checked",false);
$('#modalupdate #externaltripupdate').prop("disabled",true);
}else{
$('#modalupdate #externaltripupdate').prop("disabled",false);
$('#modalupdate #externaltripupdate').prop("checked",externaltrip);
}
$("#modalupdate .alert").remove(); $("#modalupdate .alert").remove();
eDayStart=eventstart.toString().split(" ")[2]
eDayEnd=eventend.toString().split(" ")[2]
if ((eDayEnd - eDayStart) >1) {
$('#modalupdate #amupdate').prop("checked",true);
$('#modalupdate #apupdate').prop("checked",true);
$('#modalupdate #amupdate').prop("disabled",true);
$('#modalupdate #apupdate').prop("disabled",true);
}else{
$('#modalupdate #amupdate').prop("disabled",false);
$('#modalupdate #apupdate').prop("disabled",false);
if (!eventallday){
eStart = eventstart.toString().split(" ")[4].split(":")[0]
//eEnd = eventend.toString().split(" ")[4].split(":")[0]
//AM
if (eStart == 09){
$('#modalupdate #amupdate').prop("checked",true);
$('#modalupdate #apupdate').prop("checked",false);
}
//AP
if (eStart == 13){
$('#modalupdate #amupdate').prop("checked",false);
$('#modalupdate #apupdate').prop("checked",true);
}
}else{
$('#modalupdate #amupdate').prop("checked",true);
$('#modalupdate #apupdate').prop("checked",true);
}
}
// Formulaire de création d'un évènement // Formulaire de création d'un évènement
$('#modalupdate').modal(); $('#modalupdate').modal();
@@ -343,6 +436,7 @@ function eventSubmit() {
am: $("#modalsubmit #amsubmit").prop("checked"), am: $("#modalsubmit #amsubmit").prop("checked"),
ap: $("#modalsubmit #apsubmit").prop("checked"), ap: $("#modalsubmit #apsubmit").prop("checked"),
astreinte: $("#modalsubmit #astreinte").prop("checked"), astreinte: $("#modalsubmit #astreinte").prop("checked"),
externaltrip: $("#modalsubmit #externaltrip").prop("checked"),
description: $("#modalsubmit #description").val() description: $("#modalsubmit #description").val()
}, },
url: "{{ path('app_event_submit') }}", url: "{{ path('app_event_submit') }}",
@@ -371,7 +465,10 @@ function eventUpdate() {
idevent: $("#modalupdate #idevent").val(), idevent: $("#modalupdate #idevent").val(),
iduser: $("#userupdate").val(), iduser: $("#userupdate").val(),
idtask: $("#taskupdate").val(), idtask: $("#taskupdate").val(),
am: $("#modalupdate #amupdate").prop("checked"),
ap: $("#modalupdate #apupdate").prop("checked"),
fgastreinte: $("#modalupdate #fgastreinte").val(), fgastreinte: $("#modalupdate #fgastreinte").val(),
externaltrip: $("#modalupdate #externaltripupdate").prop("checked"),
description: $("#modalupdate #description").val() description: $("#modalupdate #description").val()
}, },
url: "{{ path('app_event_update') }}", url: "{{ path('app_event_update') }}",
@@ -419,7 +516,6 @@ function eventDelete() {
// On change astreinte // On change astreinte
$("#astreinte").change(function() { $("#astreinte").change(function() {
console.log(allDay)
if(this.checked) { if(this.checked) {
$("#amsubmit").prop("disabled",true); $("#amsubmit").prop("disabled",true);
$("#apsubmit").prop("disabled",true); $("#apsubmit").prop("disabled",true);

View File

@@ -0,0 +1,8 @@
{% block body %}
Utilisateur;Jour;Journée entière;Astreinte;AM;AP;Taches;AM;Tache AM;AP;Tache AP;Astreinte;Description Astreintes
{% for user in users %}
{% for day, event in user.events %}
{{ user.user.displayname }};{{day}};{{event.allday}};{{event.astreinte}};{{event.am}};{{event.ap}};{{event.descriptionday|trim}} {{event.descriptionam|trim}} {{event.descriptionap|trim}} {{event.descriptionastreinte|trim}}
{% endfor %}
{% endfor %}
{% endblock %}

View File

@@ -0,0 +1,6 @@
{% block body %}
Tâche;Astr.Act;H.Supp;Utilisateur;Début;Fin;Durée;Description;
{% for timer in timers %}
{{timer.taskname}};{{timer.activepenalty}};{{timer.additionalhour}};{{timer.user}};{{timer.start|date("d/m/Y H:i")}};{{timer.end|date("d/m/Y H:i")}};{{timer.duration|date("H:i")}};{{timer.description}};
{% endfor %}
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% block body %}
Client;Projet;Tâche;Utilisateur;Année;Semaine;Cumul;
{% for project in projects %}
{% if project.weeks_by_task_by_user is defined %}
{% for year,weeks in project.weeks_by_task_by_user %}
{% for week in weeks %}
{% for task in week.tasks%}
{% for user in task.users%}
{{project.customer}};{{project.name}};{{task.taskname}};{{user.displayname}};{{year}};S{{week.weeknumber}};{{user.cumul|replace({".": ","})}};
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
{% endblock %}

View File

@@ -0,0 +1,36 @@
{% extends "base.html.twig" %}
{% block body %}
<h1 class="page-header">
EXPORTS DE DONNEES
</h1>
<div class="card">
<div class="card-header">
<a href="{{ path('export_project_weekly') }}" class="btn btn-success">Export des Projets par semaine par participant</a>
</div>
<div class="card-body">
<p>Exporter le cumul des points éffectués sur un/des projets, par tâche par utilisateur.</p>
<p>Filtres utiles : Nombres de mois, Projet, Service</p>
</div>
</div>
<p></p>
<div class="card">
<div class="card-header">
<a class="btn btn-success" href={{ path('app_export_penalty_additional') }}>Export des astreintes actives et heures supplémentaires</a>
</div>
<div class="card-body">
<p>Exporter la liste des astreintes actives réalisés, ainsi que les saisies en heure supplémentaires.</p>
<p>Filtres utiles : Intervenant (hors "Tout le monde")</p>
</div>
</div>
<p></p>
<div class="card">
<div class="card-header">
<a class="btn btn-success" href={{ path('export_full_worked_days') }}>Export des jours pleins travaillés</a>
</div>
<div class="card-body">
<p>Exporter la liste des jours travaillés pleinement éligibles aux tickets restaurants</p>
<p>Filtres utiles : Nombre de mois, Intervenant</p>
</div>
</div>
<p></p>
{% endblock %}

View File

@@ -0,0 +1,25 @@
{% block body %}
{% autoescape %}
<h2>Date de la demande : {{ date|date("d/m/Y H:i") }} </h2>
<p>
<b>Utilisateur =</b> {{ username }}
</p>
<p>
<b>Début =</b> {{ start|date("d/m/Y H:i") }}
</p>
<p>
<b>Fin =</b> {{ end|date("d/m/Y H:i") }}
</p>
<p>
<b>Durée =</b> {{ duration }}
</p>
<p>
<b>Type =</b> Congé
</p>
<p>
<b>Lien pour valider =</b> {{ valid_link }}
</p>
{% endautoescape %}
{% endblock %}

View File

@@ -0,0 +1,23 @@
{% block body %}
{% autoescape %}
<h2>VALIDATION</h2>
<p>
<b>Utilisateur =</b> {{ username }}
</p>
<p>
<b>Début =</b> {{ start|date("d/m/Y H:i") }}
</p>
<p>
<b>Fin =</b> {{ end|date("d/m/Y H:i") }}
</p>
<p>
<b>Durée =</b> {{ duration }}
</p>
<p>
<b>Type =</b> Congé
</p>
{% endautoescape %}
{% endblock %}

View File

@@ -18,7 +18,7 @@
{% block body %} {% block body %}
<h1 class="page-header"> <h1 class="page-header">
PROPOSITIONS COMMANDES
</h1> </h1>
<a class="btn btn-success" href={{ path('app_offer_submit') }}>Ajouter</a> <a class="btn btn-success" href={{ path('app_offer_submit') }}>Ajouter</a>

View File

@@ -69,7 +69,12 @@
<tr> <tr>
<td class="no-print"> <td class="no-print">
<a href="{{path("app_project_update",{id:project.id})}}"><i class="fa fa-file"></i></a> <a href="{{path("app_project_update",{id:project.id})}}"><i class="fa fa-file"></i></a>
<a href="{{path("app_project_users",{id:project.id})}}"><i class="fa fa-users"></i></a> <a href="{{path("app_project_users",{id:project.id})}}"
data-toggle="tooltip"
data-placement="right"
data-html="true"
title="{% for user in project.userprojects %}<b>{{user.user.username}}:</b> {{user.job.name}} <br />{% endfor %}"
><i class="fa fa-users"></i></a>
</td> </td>
<td>{{project.customer.name}}</td> <td>{{project.customer.name}}</td>
@@ -89,7 +94,7 @@
{% for task in project.tasks %} {% for task in project.tasks %}
{% set tottask=tottask+task.quantity %} {% set tottask=tottask+task.quantity %}
{% set totvalidate=totvalidate+task.validate %}
{% set totplanified=totplanified+task.validate %} {% set totplanified=totplanified+task.validate %}
{% for event in task.events %} {% for event in task.events %}
{% set totplanified=totplanified+event.duration %} {% set totplanified=totplanified+event.duration %}
@@ -130,6 +135,9 @@
{% endblock %} {% endblock %}
{% block localjavascript %} {% block localjavascript %}
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
$(document).ready(function() { $(document).ready(function() {
{% if not fgprint is defined or not fgprint %} {% if not fgprint is defined or not fgprint %}
$('.table').DataTable({ $('.table').DataTable({

View File

@@ -93,7 +93,6 @@
{% block body %} {% block body %}
{% if fgprint is defined and fgprint %}<h1>Planning</h1>{%endif%} {% if fgprint is defined and fgprint %}<h1>Planning</h1>{%endif%}
{% if access=="customer" and not app.user %} {% if access=="customer" and not app.user %}
<div class="no-print" style="margin-top:10px;"> <div class="no-print" style="margin-top:10px;">
<style> .select2-container { display:inline-block} </style> <style> .select2-container { display:inline-block} </style>
@@ -125,40 +124,9 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<h4>RAPPORT</h4> <h3>RAPPORT</h3>
<div class="small"> <div class="small">
{% if project.hors!=0 %}
consommé avant = {{ project.hors }}<br>
{% endif %}
{% set bycolonne = max(10,((project.before|length)/3)|round) %}
{% set compteur = 0 %}
<div class="row">
<div class="col-md-4">
{% for month in project.before %}
{% set compteur = compteur + 1 %}
{% if compteur > bycolonne %}
</div>
<div class="col-md-4">
{% set compteur = 1 %}
{% endif %}
consommé le {{ month.monthlabel }} = {{ month.duration }}<br>
{% endfor %}
</div>
</div>
</div>
{% if not project.beforeastreinte is empty %}
<h4>ASTREINTE</h4>
{% set compteur = 0 %}
<div class="small">
{% for month in project.beforeastreinte %}
consommé le {{ month.monthlabel }} = {{ month.duration }}<br>
{% endfor %}
</div>
{% endif %}
<div class="new-page">&nbsp;</div> <div class="new-page">&nbsp;</div>
{% for month in project.months %} {% for month in project.months %}
@@ -221,10 +189,37 @@
</table> </table>
{% endif %} {% endif %}
<div class="new-page">&nbsp;</div>
{% endfor %} {% endfor %}
<h4>CUMUL HEBDOMADAIRE</h4>
<table>
{% for year, weeks in project.weeks %}
<thead>
<th class="text-center day">
{{ year}}
</th>
{% for week in weeks %}
<th class="text-center">
S{{ week.weeknumber}}
</th>
{% endfor %}
</thead>
<tr class="text-center">
<td class="text-center">
</td>
{% for week in weeks %}
<td class="text-center">
{{ week.cumul}}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<div class="new-page">&nbsp;</div>
{% if not project.offers is empty %} {% if not project.offers is empty %}
<h4>COMMANDES</h4> <h4>COMMANDES</h4>
{% set count=(project.offers|length)-8 %} {% set count=(project.offers|length)-8 %}
@@ -334,7 +329,64 @@
<div class="new-page">&nbsp;</div> <div class="new-page">&nbsp;</div>
{% endif %} {% endif %}
<h4>CONSOMMATION PASSEE</h4>
{% if project.hors!=0 %}
Consommation précédente totale = {{ project.hors }}<br>
{% endif %}
<table>
{% for year in project.before %}
<thead>
<th class="text-center day">
Date
</th>
{% for month in year %}
<th class="text-center">
{{ month.monthlabel }}
</th>
{% endfor %}
</thead>
<tr class="text-center">
<td class="text-center">
Consommé
</td>
{% for month in year %}
<td class="text-center">
{{ month.duration }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% if not project.beforeastreinte is empty %}
<h4>ASTREINTES PASSEES</h4>
<table>
{% for year in project.beforeastreinte %}
<thead>
<th class="text-center day">
Date
</th>
{% for month in year %}
<th class="text-center">
{{ month.monthlabel }}
</th>
{% endfor %}
</thead>
<tr class=" text-center">
<td class="text-center">
Consommé
</td>
{% for month in year %}
<td class="text-center">
{{ month.duration }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endif %}
</div> </div>
</div> </div>
{% endif %} {% endif %}

View File

@@ -66,6 +66,7 @@
<div class="card-body"> <div class="card-body">
<table> <table>
<thead> <thead>
<th>S</th>
<th>L</th> <th>L</th>
<th>M</th> <th>M</th>
<th>M</th> <th>M</th>
@@ -78,8 +79,8 @@
{% for event in user.events %} {% for event in user.events %}
{% if nbday==1 %} {% if nbday==1 %}
<tr> <tr>
<td class="date" style="vertical-align:middle">{{event.date|date("W")}}</td>
{% endif %} {% endif %}
<td> <td>
<div class="date">{{ event.date | date("d/m") }}</div> <div class="date">{{ event.date | date("d/m") }}</div>
{% if event.allday %} {% if event.allday %}

View File

@@ -0,0 +1,112 @@
{% extends "base.html.twig" %}
{% block localstyle %}
td {
padding:5px !important;
}
{% endblock %}
{% block body %}
{{ form_start(form) }}
<h1 class="page-header">
{% if mode=="update" %}
Modification TIMER
{% elseif mode=="submit" %}
Création TIMER
{% endif %}
</h1>
{{ form_widget(form.submit) }}
<a class="btn btn-secondary" href={{ path('app_timer') }}>Annuler</a>
{% if mode=="update" %}
<a href="{{ path('app_timer_delete',{'id':timer.id}) }}"
class="btn btn-danger float-right"
data-method="delete"
data-confirm="Êtes-vous sûr de vouloir supprimer cet entregistrement ?">
Supprimer
</a>
{% endif %}
<br><br>
{% if app.session.flashbag.has('error') %}
<div class='alert alert-danger' style='margin: 5px 0px'>
<strong>Erreur</strong><br>
{% for flashMessage in app.session.flashbag.get('error') %}
{{ flashMessage }}<br>
{% endfor %}
</div>
{% endif %}
{% if app.session.flashbag.has('notice') %}
<div class='alert alert-info' style='margin: 5px 0px'>
<strong>Information</strong><br>
{% for flashMessage in app.session.flashbag.get('notice') %}
{{ flashMessage }}<br>
{% endfor %}
</div>
{% endif %}
<div class="card">
<div class="card-header">
<i class="fa fa-pencil-alt fa-fw"></i> Informations
</div>
<div class="card-body">
{{ form_row(form.task) }}
{{ form_row(form.description) }}
{{ form_row(form.activepenalty) }}
{{ form_row(form.additionalhour) }}
{{ form_row(form.start) }}
{{ form_row(form.end) }}
{{ form_row(form.duration) }}
</div>
</div>
{{ form_end(form) }}
{% endblock %}
{% block localjavascript %}
$("#timer_task").addClass("select2entity");
/*
* Pads this string with another string on the left until the resulting string
* has specified length. If the padding string has more than one character, the
* resulting string may be longer than desired (the padding string is not
* truncated and it is only prepended as a whole). Bad API, I know, but it's
* good enough for me.
*/
String.prototype.pad = function(length, padding) {
var result = this;
while (result.length < length) {
result = padding + result;
}
return result;
}
/* Some time constants. */
var MILISECONDS_IN_SECOND = 1000;
var MILISECONDS_IN_MINUTE = 60 * MILISECONDS_IN_SECOND;
var MINUTES_IN_HOUR = 60;
/* Formats the time in the H:MM format. */
function formatTime(time) {
var timeInMinutes = time / MILISECONDS_IN_MINUTE;
var hours = Math.floor(timeInMinutes / MINUTES_IN_HOUR);
var minutes = Math.floor(timeInMinutes - hours * MINUTES_IN_HOUR);
return String(hours).pad(2, "0") + ":" + String(minutes).pad(2, "0");
}
$("#timer_start_time,#timer_end_time").on('input',function(){
console.log($("#timer_start_date").val() +"T"+$("#timer_start_time").val()+":00Z")
console.log($("#timer_end_date").val()+"T"+$("#timer_end_time").val()+":00Z")
var start = Date.parse($("#timer_start_date").val() +"T"+$("#timer_start_time").val()+":00Z");
var end = Date.parse($("#timer_end_date").val()+"T"+$("#timer_end_time").val()+":00Z");
var diff = end - start;
$("#timer_duration").val(formatTime(diff))
})
{% endblock %}

View File

@@ -0,0 +1,186 @@
{% extends "base.html.twig" %}
{% block head_style %}
{{ encore_entry_link_tags('app') }}
{{ encore_entry_link_tags('fullcalendar') }}
{% endblock head_style %}
{% block localstyle %}
.fc-header-toolbar h2 {
text-transform: uppercase;
}
.fc-day-grid-event {
padding:0px;
border-radius:0px;
border: none;
}
.fc-content {
height: 40px;
}
.fc-title {
font-weight: bolder;
font-size: 12px;
}
.eventAvatar {
width: 20px;
margin: 0px 5px 0px 0px;
float: left;
}
.eventInfo{
margin: -5px 5px 0px 0px;
clear: both;
}
.eventUser{
clear: both;
}
.eventEstimate {
margin: -3px 10px;
}
{% endblock %}
{% block body %}
<div id="fullcalendar" style="width:100%; margin-top:10px;"></div>
{{ encore_entry_script_tags('fullcalendar') }}
{% endblock %}
{% block localjavascript %}
$(document).ready(function() {
$("#modalsubmit #user").select2({
theme: 'bootstrap4',
language: "fr"
});
$("#modalsubmit #task").select2({
placeholder: "Selectionnez un projet",
allowClear: true,
theme: 'bootstrap4',
language: "fr"
});
$("#modalupdate #user").select2({
theme: 'bootstrap4',
language: "fr"
});
$("#modalupdate #task").select2({
placeholder: "Selectionnez un projet",
theme: 'bootstrap4',
language: "fr"
});
});
// Rendu d'un évenement
function eventRender(info) {
console.log(info.event.extendedProps);
// Récupération des divers élements du rendu event
var content=$(info.el).children('.fc-content');
var title=$(content).children('.fc-title');
// Ajouter l'avatar
content.prepend("<img src="+info.event.extendedProps.avatar+" class='eventAvatar'>");
content.append("<span class='eventUser float-left small'>"+info.event.extendedProps.username+"</span>");
var eventInfo=$(content).children('.eventUser');
// Ajout container
content.append("<span class='eventInfo float-right'></span>");
var eventInfo=$(content).children('.eventInfo');
// Ajouter le verrou si event non editable
if(info.event.extendedProps.locked) {
eventInfo.append("<i class='fa fa-lock float-right'></i>");
}
// Ajout estimation
eventInfo.append("<span class='eventEstimate float-right small'>"+info.event.extendedProps.estimate+"</span>");
// Description
content.attr("title",info.event.extendedProps.fulldescription);
}
// Formulaire Création d'un évelement
var allDay;
function eventSelect(selectionInfo) {
var start=moment(selectionInfo.start);
var end=moment(selectionInfo.end);
var end=end.subtract(1, 'd');
allDay=(start.format("DD/MM/YYYY") != end.format("DD/MM/YYYY"));
// Controle
if(start.month()!=end.month()) {
alert("Une tâche ne peut être sur deux mois différents");
return false;
}
if(start.week()!=end.week()) {
alert("Une tâche ne peut être sur deux semaines différentes");
return false;
}
// Valeur par défaut
{% if (is_granted('ROLE_ADMIN') or is_granted('ROLE_VALIDATOR') or is_granted('ROLE_MASTER')) and app.session.get('iduser')!="all" %}
$('#usersubmit').val({{app.session.get('iduser')}}).trigger("change");
{% else %}
$('#usersubmit').val({{app.user.id}}).trigger("change");
{% endif %}
// Si jour de fin un samedi ou un dimanche : on est forcement en astreinte
if(moment(end).day()==0||moment(end).day()==6) {
$("#modalsubmit #astreinte").prop("checked",true);
$("#modalsubmit #astreinte").attr("disabled",true);
allDay=true;
}
else {
$("#modalsubmit #astreinte").prop('checked', false);
$("#modalsubmit #astreinte").attr('disabled', false);
}
$('#modalsubmit #amsubmit').prop("checked",true);
$('#modalsubmit #apsubmit').prop("checked",true);
$('#modalsubmit #amsubmit').attr("disabled",allDay);
$('#modalsubmit #apsubmit').attr("disabled",allDay);
$('#modalsubmit #start').val(start.format("YYYY-MM-DD"));
$('#modalsubmit #end').val(end.format("YYYY-MM-DD"));
$('#modalsubmit #description').val("");
$("#modalsubmit .alert").remove();
// Formulaire de création d'un évènement
$('#modalsubmit').modal();
}
// On change astreinte
$("#astreinte").change(function() {
console.log(allDay)
if(this.checked) {
$("#amsubmit").prop("disabled",true);
$("#apsubmit").prop("disabled",true);
$('#modalsubmit #amsubmit').prop("checked",true);
$('#modalsubmit #apsubmit').prop("checked",true);
}
else {
$("#amsubmit").prop("disabled",allDay);
$("#apsubmit").prop("disabled",allDay);
$('#modalsubmit #amsubmit').prop("checked",true);
$('#modalsubmit #apsubmit').prop("checked",true);
}
});
{% endblock %}

View File

@@ -0,0 +1,703 @@
{% extends "base.html.twig" %}
{% block localstyle %}
#timer-task {
width:300px;
}
#timer-desc {
width:300px;
}
{% endblock %}
{% block body %}
<h1 class="page-header">
SUIVI HORAIRE
</h1>
<div id="timer" class="card">
<div class="card-header">
<a class="btn btn-success" style="float:right" href={{ path('app_timer_submit') }}>Créer un Timer</a>
Lancer un Timer :
<select class="select2entity" id="timer-task" name="timer-task">
<option> Tâche </option>
{% for task in tasks %}
<option value="{{task.id}}">{{task.displayname}}</option>
{% endfor %}
</select>
<input id="timer-desc" name="timer-desc" placeholder="Description" />
<a href='#' title='Add' id='addtimer'>
<i class='fas fa-plus-circle'></i>
</a>
</div>
</div>
<div>
<table id="task-table" class="table table-striped table-hover" >
</table>
</div>
<a class="btn btn-success" href={{ path('app_timer_view') }}>Voir le calendrier</a>
<p></p>
<div class="dataTable_wrapper">
<table class="table table-striped table-bordered table-hover small" id="dataTables" style="width:100%">
<thead>
<tr>
<th width="70px" class="no-sort">Tâche</th>
<th width="150px" class="no-sort">Description</th>
<th width="150px">Début</th>
<th width="100px">Fin</th>
<th width="100px">Durée</th>
<th width="100px" class="text-center no-string">Actions</th>
</tr>
</thead>
<tbody>
{%for timer in timers %}
<tr>
<td>{{ timer.task.displayname }}</td>
<td>
<span class="font-weight-bold">
{{ timer.activepenalty ? "<i class='fa fa-exclamation' style='color:red;opacity: 0.33;'></i> Astreinte active" : "" }}
</span>
<span class="font-weight-bold">
{{ timer.additionalhour ? "<i class='fa fa-exclamation-triangle' style='color:red;opacity: 0.33;'></i> Heures supplémentaires" : "" }}
</span>
<p>{{ timer.description }}</p>
</td>
<td>{{ timer.start|date("d/m/Y H:i") }}</td>
<td>{{ timer.end|date("d/m/Y H:i") }}</td>
<td>{{ timer.duration|date("H:i") }}</td>
<td>
<a href="{{path("app_timer_update",{id:timer.id})}}">
<i class="fa fa-file"></i>
</a>
&nbsp;&nbsp;
<a href="{{ path('app_timer_delete',{'id':timer.id}) }}"
data-method="delete"
data-confirm="Êtes-vous sûr de vouloir supprimer cet entregistrement ?">
<i class="fa fa-trash-alt"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
{% block localjavascript %}
/* Creates a new Task object. */
function Task(id ,name, description) {
this._name = name;
this._taskid = id;
this._description = description;
this._state = Task.State.STOPPED;
this._timeSpentInPreviousIterations = 0;
this._startDate = 0;
this._endDate = 0;
this._currentIterationStartTime = 0;
this._customTimeSpent = 0;
this._onChange = null;
}
/* Possible task states. */
Task.State = {
STOPPED: "stopped",
RUNNING: "running"
}
Task.prototype = {
/* Returns the task name. */
getName: function() {
return this._name;
},
/* Returns the task description. */
getDescription: function() {
if (this._description == "NaN") {
this._description = ""
}
return this._description;
},
/* Returns the task state. */
getState: function() {
return this._state;
},
/* Is the task stopped? */
isStopped: function() {
return this._state == Task.State.STOPPED;
},
/* Is the task running? */
isRunning: function() {
return this._state == Task.State.RUNNING;
},
/*
* Sets the "onChange" event handler. The "onChange" event is fired when the
* task is started, stopped, or reset.
*/
setOnChange: function(onChange) {
this._onChange = onChange;
},
/*
* Returns the time spent on the task in the current work iteration. Works
* correctly only when the task is running.
*/
_getCurrentIterationTime: function() {
return (new Date).getTime() - this._currentIterationStartTime;
},
/*
* Returns the total time spent on the task. This includes time spent in
* the current work iteration if the task is running.
*/
getTimeSpent: function() {
var result = this._timeSpentInPreviousIterations;
if (this._state == Task.State.RUNNING) {
result += this._getCurrentIterationTime();
}
return result;
},
/* Calls the "onChange" event handler if set. */
_callOnChange: function() {
if (typeof this._onChange == "function") {
this._onChange();
}
},
/* Starts a new task work iteration. */
start: function() {
if (this._state == Task.State.RUNNING) { return };
if (this._startDate == 0) {this._startDate = new Date()}
this._state = Task.State.RUNNING;
this._currentIterationStartTime = (new Date).getTime();
this._callOnChange();
},
/* Stops the current task work iteration. */
stop: function() {
if (this._state == Task.State.STOPPED) { return };
this._state = Task.State.STOPPED;
this._timeSpentInPreviousIterations += this._getCurrentIterationTime();
this._currentIterationStartTime = 0;
this._endDate = new Date();
this._callOnChange();
},
/* Stops the current task work iteration and resets the time data. */
reset: function() {
this.stop();
this._timeSpentInPreviousIterations = 0;
this._callOnChange();
},
save: function(task){
console.log(task)
$.ajax({
type: "POST",
data: {
taskname: task._name,
taskid: task._taskid,
description: task._description,
start: task._startDate,
end: task._endDate,
duration: task._timeSpentInPreviousIterations,
},
url: "{{ path('app_timer_create') }}",
success: function (response) {
response=JSON.parse(response);
if(response.return=="KO") {
$("#dataTable_wrapper").append("<div class='alert alert-danger' style='margin: 5px 0px'>"+response.error+"</div>");
}else{
location.reload();
}
}
});
},
/* Serializes the task into a string. */
serialize: function() {
/*
* Originally, I wanted to use "toSource" and "eval" for serialization and
* deserialization, but "toSource" is not supported by WebKit, so I resorted
* to ugly hackery...
*/
return [
encodeURIComponent(this._name),
this._state,
this._timeSpentInPreviousIterations,
this._currentIterationStartTime,
this._startDate,
this._endDate,
this._taskid,
this._description,
].join("&");
},
/* Deserializes the task from a string. */
deserialize: function(serialized) {
var parts = serialized.split("&");
this._name = decodeURIComponent(parts[0]);
this._state = parts[1];
this._timeSpentInPreviousIterations = parseInt(parts[2]);
this._currentIterationStartTime = parseInt(parts[3]);
this._startDate = parseInt(parts[4]);
this._endDate = parseInt(parts[5]);
this._taskid = parseInt(parts[6]);
this._description = parseInt(parts[7]);
}
}
/* ===== Tasks ===== */
/* The Tasks class represents a list of tasks. */
/* Creates a new Tasks object. */
function Tasks() {
this._tasks = [];
this._onAdd = null;
this._onRemove = null;
}
Tasks.prototype = {
/*
* Sets the "onAdd" event handler. The "onAdd" event is fired when a task
* is added to the list.
*/
setOnAdd: function(onAdd) {
this._onAdd = onAdd;
},
/*
* Sets the "onRemove" event handler. The "onRemove" event is fired when a
* task is removed from the list.
*/
setOnRemove: function(onRemove) {
this._onRemove = onRemove;
},
/* Returns the length of the task list. */
length: function() {
return this._tasks.length
},
/*
* Returns index-th task in the list, or "undefined" if the index is out of
* bounds.
*/
get: function(index) {
return this._tasks[index];
},
/*
* Calls the callback function for each task in the list. The function is
* called with three parameters - the task, its index and the task list
* object. This is modeled after "Array.forEach" in JavaScript 1.6.
*/
forEach: function(callback) {
for (var i = 0; i < this._tasks.length; i++) {
callback(this._tasks[i], i, this);
}
},
/* Calls the "onAdd" event handler if set. */
_callOnAdd: function(task) {
if (typeof this._onAdd == "function") {
this._onAdd(task);
}
},
/* Adds a new task to the end of the list. */
add: function(task) {
this._tasks.push(task);
this._callOnAdd(task);
},
/* Calls the "onRemove" event handler if set. */
_callOnRemove: function(index) {
if (typeof this._onRemove == "function") {
this._onRemove(index);
}
},
/*
* Removes index-th task from the list. Does not do anything if the index
* is out of bounds.
*/
remove: function(index) {
this._callOnRemove(index);
this._tasks.splice(index, 1);
},
/* Serializes the list of tasks into a string. */
serialize: function() {
var serializedTasks = [];
this.forEach(function(task) {
serializedTasks.push(task.serialize());
});
return serializedTasks.join("|");
},
/* Deserializes the list of tasks from a string. */
deserialize: function(serialized) {
/*
* Repeatedly use "remove" so the "onRemove" event is triggered. Do the same
* with the "add" method below.
*/
while (this._tasks.length > 0) {
this.remove(0);
}
var serializedTasks = serialized.split("|");
for (var i = 0; i < serializedTasks.length; i++) {
var task = new Task(0 ,"", "");
task.deserialize(serializedTasks[i]);
this.add(task);
}
}
}
/* ===== Extensions ===== */
/*
* Pads this string with another string on the left until the resulting string
* has specified length. If the padding string has more than one character, the
* resulting string may be longer than desired (the padding string is not
* truncated and it is only prepended as a whole). Bad API, I know, but it's
* good enough for me.
*/
String.prototype.pad = function(length, padding) {
var result = this;
while (result.length < length) {
result = padding + result;
}
return result;
}
/* ===== Task List + DOM Storage ===== */
/* The list of tasks. */
var tasks = new Tasks();
/* The last value of the serialized task list string. */
var lastSerializedTasksString;
/*
* The key under which the serialized task list string is stored in the DOM
* Storage.
*/
var TASKS_DOM_STORAGE_KEY = "timerTasks";
/*
* Returns DOM Storage used by the application, or "null" if the browser does
* not support DOM Storage.
*/
function getStorage() {
/*
* HTML 5 says that the persistent storage is available in the
* "window.localStorage" attribute, however Firefox implements older version
* of the proposal, which uses "window.globalStorage" indexed by the domain
* name. We deal with this situation here as gracefully as possible (i.e.
* without concrete browser detection and with forward compatibility).
*
* For more information, see:
*
* http://www.whatwg.org/specs/web-apps/current-work/#storage
* https://developer.mozilla.org/En/DOM/Storage
*/
if (window.localStorage !== undefined) {
return window.localStorage;
} else if (window.globalStorage !== undefined) {
return window.globalStorage[location.hostname];
} else {
return null;
}
}
/*
* Saves the task list into a DOM Storage. Also updates the value of the
* "lastSerializedTasksString" variable.
*/
function saveTasks() {
var serializedTasksString = tasks.serialize();
getStorage()[TASKS_DOM_STORAGE_KEY] = serializedTasksString;
lastSerializedTasksString = serializedTasksString;
}
/*
* Loads the serialized task list string from the DOM Storage. Returns
* "undefined" if the storage does not contain the string (this happens when
* running the application for the first time).
*/
function loadSerializedTasksString() {
var storedValue = getStorage()[TASKS_DOM_STORAGE_KEY];
/*
* The spec says "null" should be returned when the key is not found, but some
* browsers return "undefined" instead. Maybe it was in some earlier version
* of the spec (I didn't bother to check).
*/
if (storedValue !== null && storedValue !== undefined && storedValue.length > 0) {
/*
* The values retrieved from "globalStorage" use one more level of
* indirection.
*/
return (window.localStorage === undefined) ? storedValue.value : storedValue;
} else {
return undefined;
}
}
/*
* Loads the task list from the DOM Storage. Also updates the value of the
* "lastSerializedTasksString" variable.
*/
function loadTasks() {
var serializedTasksString = loadSerializedTasksString();
if (serializedTasksString !== undefined) {
tasks.deserialize(serializedTasksString);
lastSerializedTasksString = serializedTasksString;
}
}
/*
* Was the task list changed outside of the application? Detects the change
* by comparing the current serialized task list string in the DOM Storage
* with a kept old value.
*/
function tasksHaveChangedOutsideApplication() {
var serializedTasksString = loadSerializedTasksString();
if (serializedTasksString != lastSerializedTasksString) {
return true
}
return false;
}
/* ===== View ===== */
/* Some time constants. */
var MILISECONDS_IN_SECOND = 1000;
var MILISECONDS_IN_MINUTE = 60 * MILISECONDS_IN_SECOND;
var MINUTES_IN_HOUR = 60;
/* Formats the time in the H:MM format. */
function formatTime(time) {
var timeInMinutes = time / MILISECONDS_IN_MINUTE;
var hours = Math.floor(timeInMinutes / MINUTES_IN_HOUR);
var minutes = Math.floor(timeInMinutes - hours * MINUTES_IN_HOUR);
return hours + ":" + String(minutes).pad(2, "0");
}
/*
* Computes the URL of the image in the start/stop link according to the task
* state.
*/
function computeStartStopLinkImageUrl(state) {
switch (state) {
case Task.State.STOPPED:
return "fa-play";
case Task.State.RUNNING:
return "fa-stop";
default:
throw "Invalid task state."
}
}
/*
* Builds the HTML element of the row in the task table corresponding to the
* specified task and index.
*/
function buildTaskRow(task, index) {
var result = $("<tr />");
var startStopLink = $(
"<a href='#' class='start-stop-link' title='Start/stop'>"
+ "<i class='fa " + computeStartStopLinkImageUrl(task.getState()) + "'></i>"
);
startStopLink.click(function() {
switch (task.getState()) {
case Task.State.STOPPED:
task.start();
break;
case Task.State.RUNNING:
task.stop();
break;
default:
throw "Invalid task state."
}
saveTasks();
return false;
});
var resetLink = $(
"<a href='#' title='Reset'>"
+ "<i class='fa fa-undo'></i>"
);
resetLink.click(function() {
task.reset();
saveTasks();
return false;
});
var deleteLink = $(
"<a href='#' title='Delete' class='deletetask'>"
+ "<i class='fa fa-trash-alt' class='deletetask'></i>"
);
deleteLink.click(function() {
if (confirm("Do you really want to delete task \"" + task.getName() + "\"?")) {
tasks.remove(index);
saveTasks();
}
return false;
});
var saveLink = $(
"<a href='#' title='Save' class='savetask'>"
+ "<i class='fa fa-download' class='savetask'></i>"
);
saveLink.click(function() {
tasks.remove(index);
saveTasks();
task.save(task);
return false;
});
result
.addClass("state-" + task.getState())
.append($("<td class='task-name' />").text(task.getName()))
.append($("<td class='task-description' />").text(task.getDescription()))
.append($("<td class='task-time' />").text(formatTime(task.getTimeSpent())))
.append($("<td class='task-actions' />")
.append(startStopLink)
.append("&nbsp;&nbsp;")
.append(resetLink)
.append("&nbsp;&nbsp;")
.append(saveLink)
.append("&nbsp;&nbsp;&nbsp;&nbsp;")
.append(deleteLink)
);
return result;
}
/* Finds row with the specified index in the task table. */
function findRowWithIndex(index) {
return $("#task-table").find("tr").slice(1).eq(index);
}
/*
* Updates the row in the task table corresponding to a task according to
* its state.
*/
function updateTaskRow(row, task) {
if (task.isStopped()) {
row.removeClass("state-running");
row.addClass("state-stopped");
} else if (task.isRunning()) {
row.removeClass("state-stopped");
row.addClass("state-running");
}
row.find(".task-time").text(formatTime(task.getTimeSpent()))
row.find(".start-stop-link i").removeClass();
row.find(".start-stop-link i").addClass("fa "+computeStartStopLinkImageUrl(task.getState()));
}
/* ===== Initialization ===== */
/* Initializes event handlers on the task list. */
function initializeTasksEventHandlers() {
tasks.setOnAdd(function(task) {
var row = buildTaskRow(task, tasks.length() - 1);
$("#task-table").append(row);
task.setOnChange(function() {
updateTaskRow(row, task);
});
});
tasks.setOnRemove(function(index) {
findRowWithIndex(index).remove();
if (tasks.length() == 1 ) {
$( "#task-table" ).css({"display":"none"});
}
});
}
/* Initializes GUI event handlers. */
function initializeGuiEventHandlers() {
$( "#addtimer" ).click(function() {
displayTaskAdd();
});
$( "#timer-desc" ).keypress(function( event ) {
if ( event.which == 13 ) {
event.preventDefault();
displayTaskAdd();
}
});
}
function displayTaskAdd() {
$( "#task-table" ).css({"display":"inline"});
var taskName = $("#timer-task option:selected").text();
var taskId = $("#timer-task option:selected").val();
var taskDesc = $("#timer-desc").val();
var task = new Task(taskId, taskName, taskDesc);
tasks.add(task);
saveTasks();
$("#timer-desc").val("");
}
/*
* Initializes a timer used to update task times and detect changes in the
* data stored in the DOM Storage.
*/
function initializeTimer() {
setInterval(function() {
tasks.forEach(function(task, index) {
updateTaskRow(findRowWithIndex(index), task);
});
if (tasksHaveChangedOutsideApplication()) {
loadTasks();
}
}, 10 * MILISECONDS_IN_SECOND);
}
/* Initializes the application. */
$(document).ready(function(){
try {
if (!getStorage()) {
alert("Timer requires a browser with DOM Storage support, such as Firefox 3+ or Safari 4+.");
return;
}
} catch (e) {
alert("Timer does not work with file: URLs in Firefox.");
return;
}
initializeTasksEventHandlers();
loadTasks();
initializeGuiEventHandlers();
initializeTimer();
});
{% endblock %}

View File

@@ -95,6 +95,32 @@
{%endif%} {%endif%}
</div> </div>
</div> </div>
<p></p>
<div class="card">
<div class="card-header">
<i class="fas fa-list-ul"></i> Affectations
</div>
<div class="card-body">
<table class="table table-striped table-bordered table-hover" id="dataTables" style="width:100%">
<thead>
<tr>
<th>Projet</th>
<th>Rôle</th>
</tr>
</thead>
<tbody>
{% for userproject in user.userprojects %}
<tr>
<td>{{userproject.project.displayname}}</td>
<td>{{userproject.job.name}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>
{{ form_end(form) }} {{ form_end(form) }}

View File

@@ -68,6 +68,7 @@
<table> <table>
<thead> <thead>
<th class="no-print"></th> <th class="no-print"></th>
<th>S</th>
<th>L</th> <th>L</th>
<th>M</th> <th>M</th>
<th>M</th> <th>M</th>
@@ -89,6 +90,7 @@
{% set btnvalidate=true %} {% set btnvalidate=true %}
{%endif%} {%endif%}
</td> </td>
<td class="date" style="vertical-align:middle">{{event.date|date("W")}}</td>
{% endif %} {% endif %}
<td> <td>

View File

@@ -0,0 +1,163 @@
{% extends "base.html.twig" %}
{% block localstyle %}
{% if fgprint is defined and fgprint %}
table { font-size:10px;}
th,td {
border: 1px solid #37474F;
}
thead {
display: table-header-group;
}
tr { page-break-inside: avoid; }
.homecard {width: 100% }
{%endif%}
{% endblock %}
{% block body %}
<h1 class="page-header">
VALIDATION HORAIRES
</h1>
<div class="custom-control custom-switch float-right">
<input type="checkbox" class="custom-control-input" id="switchactive" {% if app.session.get('activetimer') %} checked {% endif %}>
<label class="custom-control-label" for="switchactive">Horaires à Valider</label>
</div>
<div style="height:30px;">
</div>
<div class="card homecard">
<div class="card-header">
{% if not app.session.get('activetimer') %}
Horaires à Dévalider
{% else %}
Horaires à Valider
{% endif %}
</div>
<div class="card-body">
<div class="dataTable_wrapper">
<table class="table table-striped table-bordered table-hover small" id="dataTables" style="width:100%">
<thead>
<th class="no-print"></th>
<th width="150px">Utilisateur</th>
<th>Tâche</th>
<th>Description</th>
<th>Début</th>
<th>Fin</th>
<th>Durée</th>
</thead>
{% for user in users %}
{% for timer in user.timers %}
<tr id="row-{{timer.id}}">
<td class="no-print" style="vertical-align:middle">
{% if timer.validate %}
<i class="fa fa-thumbs-down validate-{{user.user.id}}" onClick="devalidate({{timer.id}})" style="cursor:pointer; color:red;"></i>
{% else %}
<i class="fa fa-thumbs-up validate-{{user.user.id}}" onClick="validate({{timer.id}})" style="cursor:pointer; color:green;"></i>
{% endif %}
</td>
<td>
{{ user.user.displayname }}
</td>
<td>
{{ timer.taskname }}
</td>
<td>
<span class="font-weight-bold">
{{ timer.activepenalty ? "<i class='fa fa-exclamation' style='color:red;opacity: 0.33;'></i> Astreinte active" : "" }}
</span>
<span class="font-weight-bold">
{{ timer.additionalhour ? "<i class='fa fa-exclamation-triangle' style='color:red;opacity: 0.33;'></i> Heures supplémentaires" : "" }}
</span>
<p>{{ timer.description }}</p>
</td>
<td>
{{ timer.start|date("d/m/Y H:i") }}
</td>
<td>
{{ timer.end|date("d/m/Y H:i") }}
</td>
<td>
{{ timer.duration|date("H:i") }}
</td>
</tr>
{% endfor %}
{% endfor %}
</table>
</div>
</div>
</div>
{% endblock %}
{% block localjavascript %}
$(document).ready(function() {
{% if not fgprint is defined or not fgprint %}
$('.table ').DataTable({
columnDefs: [ { "targets": "no-sort", "orderable": false }, { "targets": "no-string", "type" : "num" } ],
responsive: true,
iDisplayLength: 100,
order: [[ 1, "asc" ]]
});
{%else%}
$('#dataTables').removeClass("table table-striped table-bordered table-hover small dataTable no-footer");
{% endif %}
});
function myprint() {
href=document.location.href;
document.location.href=href+"?fgprint=true";
}
function validate(id) {
$.ajax({
type: "POST",
data: {
id: id,
},
url: "{{ path('app_validationtimer_validate') }}",
success: function (response) {
response=JSON.parse(response);
if(response.return=="KO") {
alert(response.error);
}
else {
$("#row-"+id).remove();
}
}
});
}
function devalidate(id) {
$.ajax({
type: "POST",
data: {
id: id,
},
url: "{{ path('app_validationtimer_devalidate') }}",
success: function (response) {
response=JSON.parse(response);
if(response.return=="KO") {
alert(response.error);
}
else {
$("#row-"+id).remove();
}
}
});
}
$('#switchactive').change(function() {
window.location="{{ path('app_validationtimer_activetimer' )}}";
});
{% endblock %}

View File

@@ -256,7 +256,7 @@
{% set contentsidebar="contentsidebar" %} {% set contentsidebar="contentsidebar" %}
<div id="sidebar" class="collapse"> <div id="sidebar" class="collapse">
<ul class="nav"> <ul style="padding-bottom:70px" class="nav">
{% if is_granted('ROLE_ADMIN') or is_granted('ROLE_VALIDATOR') or is_granted('ROLE_MASTER') or is_granted('ROLE_USER') %} {% if is_granted('ROLE_ADMIN') or is_granted('ROLE_VALIDATOR') or is_granted('ROLE_MASTER') or is_granted('ROLE_USER') %}
<p></p> <p></p>
<li> <li>
@@ -284,13 +284,21 @@
</label> </label>
</a> </a>
<div class="select-control"> <div class="select-control">
<select class="form-control select2entity" id="sideuser" name="sideuser"> <select class="form-control select2entity" id="sideuser" name="sideuser" multiple>
<option value="all" selected>Tout le monde</option> <option value="all" {%if app.session.get('iduser')=="all" %}selected{% endif %}>Tout le monde</option>
{% for user in app.session.get('users') %} {% for user in app.session.get('users') %}
{% set selected="" %} {% set selected="" %}
{% if app.session.get('selectedusers')|length > 1 %}
{% for suser in app.session.get('selectedusers') %}
{%if user.id == suser %}
{% set selected="selected" %}
{% endif %}
{% endfor %}
{% else %}
{%if user.id==app.session.get('iduser') %} {%if user.id==app.session.get('iduser') %}
{% set selected="selected" %} {% set selected="selected" %}
{% endif %} {% endif %}
{% endif %}
<option value="{{user.id}}" {{selected}}>{{user.displayname}}</option> <option value="{{user.id}}" {{selected}}>{{user.displayname}}</option>
{% endfor %} {% endfor %}
</select> </select>
@@ -359,9 +367,21 @@
</a> </a>
</li> </li>
<li class="last"> <li>
<a href="{{path("app_timer")}}">
<i class="fa fa-stopwatch"></i>Suivi Horaire
</a>
</li>
<li>
<a href="{{path("app_holiday")}}"> <a href="{{path("app_holiday")}}">
<i class="fa fa-cocktail"></i>Mes Congés <i class="fa fa-umbrella-beach"></i>Mes Congés
</a>
</li>
<li class="last">
<a href="{{path("app_export_view")}}">
<i class="fa fa-file-download"></i>Exports
</a> </a>
</li> </li>
@@ -372,13 +392,19 @@
<li> <li>
<a href="{{path("app_validation")}}"> <a href="{{path("app_validation")}}">
<i class="fa fa-user"></i>Validation <i class="fa fa-user-check"></i>Validation Calendrier
</a>
</li>
<li>
<a href="{{path("app_validationholiday")}}">
<i class="fa fa-user-tag"></i>Validation Congés
</a> </a>
</li> </li>
<li class="last"> <li class="last">
<a href="{{path("app_validationholiday")}}"> <a href="{{path("app_validationtimer")}}">
<i class="fa fa-suitcase"></i>Validation Congés <i class="fa fa-user-clock"></i>Validation Horaires
</a> </a>
</li> </li>
@@ -401,7 +427,7 @@
<li> <li>
<a href="{{path("app_offer")}}"> <a href="{{path("app_offer")}}">
<i class="fa fa-euro-sign"></i>Propositions <i class="fa fa-euro-sign"></i>Commandes
</a> </a>
</li> </li>
@@ -494,10 +520,13 @@
$('#sideuser').on('select2:select', function (e) { $('#sideuser').on('select2:select', function (e) {
var data = e.params.data; var data = e.params.data;
var selectedusers = $('#sideuser').val();
console.log(selectedusers)
$.ajax({ $.ajax({
type: "POST", type: "POST",
data: { data: {
iduser: data.id, iduser: data.id,
selectedusers: selectedusers,
}, },
url: "{{ path('app_home_selectuser') }}", url: "{{ path('app_home_selectuser') }}",
success: function (response) { success: function (response) {

View File

@@ -4,6 +4,12 @@ APP_SECRET=%%pwdreader("","/var/www/html/schedule/.key")
APP_AUTH=CAS APP_AUTH=CAS
# MAIL sendmail / smtp
MAILER_METHOD=sendmail
MAILER_URL=
MAILER_NOREPLY=noreply@noreply.fr
MAILER_DEFAULT_NOTIF=%%getVar('schedule_email_global_notif', '')
# Bdd = Redefine local # Bdd = Redefine local
DATABASE_NAME=schedule DATABASE_NAME=schedule
DATABASE_USER=schedule DATABASE_USER=schedule