r365 - in trunk/chorem-webmotion/src/main: java/org/chorem/webmotion/actions/project resources webapp/WEB-INF/jsp
Author: meynier Date: 2013-07-17 14:11:46 +0200 (Wed, 17 Jul 2013) New Revision: 365 Url: http://chorem.org/projects/chorem/repository/revisions/365 Log: Added Employee dashboard and enhanced project dashboard with new data organisation Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationData.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/TaskData.java trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/Calculation.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java trunk/chorem-webmotion/src/main/resources/mapping trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/Calculation.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/Calculation.java 2013-07-16 15:41:06 UTC (rev 364) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/Calculation.java 2013-07-17 12:11:46 UTC (rev 365) @@ -78,7 +78,7 @@ /** - * Return the percentages for the current object + * Return the times (in hour) per employee for the current object * @return */ protected abstract HashMap<Employee, Double> getTimes(); @@ -265,4 +265,8 @@ else return realSrp; } + + public T getObject() { + return e; + } } Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java 2013-07-16 15:41:06 UTC (rev 364) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java 2013-07-17 12:11:46 UTC (rev 365) @@ -1,6 +1,8 @@ package org.chorem.webmotion.actions.project; +import java.io.ObjectInputStream.GetField; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; @@ -8,9 +10,11 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; import org.apache.commons.logging.Log; @@ -22,11 +26,14 @@ import org.debux.webmotion.server.render.Render; import org.chorem.entities.Closed; import org.chorem.entities.Configuration; +import org.chorem.entities.Employee; import org.chorem.entities.Interval; import org.chorem.entities.Quotation; import org.chorem.entities.Project; import org.chorem.entities.Task; import org.chorem.entities.TaskStatus; +import org.chorem.entities.Time; +import org.chorem.entities.Worker; import org.nuiton.wikitty.entities.Wikitty; import org.nuiton.wikitty.entities.WikittyExtension; import org.nuiton.wikitty.query.WikittyQuery; @@ -57,49 +64,25 @@ //Fetch the quotation from the filter WikittyQuery quotationQuery = new WikittyQueryMaker().ideq(quotationFilter).end(); WikittyQueryResult<Quotation> quotationResult = client.findAllByQuery(Quotation.class, quotationQuery); + + HashMap<Project, List<QuotationData>> projectData = new HashMap<Project, List<QuotationData>>(); //If some quotation has been found - if(quotationResult != null) { + if(quotationResult != null && quotationResult.size() != 0) { Quotation quotation = quotationResult.get(0); + //Fetch the quotation's project WikittyQuery projectQuery = new WikittyQueryMaker().ideq(quotation.getProject()).end(); WikittyQueryResult<Project> projectResult = client.findAllByQuery(Project.class, projectQuery); - - HashMap<Project, List<Quotation>> quotations = new HashMap<Project, List<Quotation>>(); - HashMap<Quotation, List<Task>> taskMap = new HashMap<Quotation, List<Task>>(); - HashMap<Task, String> alertsMap = new HashMap<Task, String>(); - HashMap<Quotation, QuotationCalculation> calculations = new HashMap<Quotation, QuotationCalculation>(); - HashMap<Task, TaskCalculation> taskCalc = new HashMap<Task, TaskCalculation>(); - - quotations.put(projectResult.get(0), quotationResult.getAll()); - Configuration config = client.findAllByQuery(Configuration.class, - (new WikittyQueryMaker()).exteq(Configuration.EXT_CONFIGURATION).end() - ).get(0); - - List<Task> tasks = fetchTasks(quotation, client); - taskMap.put(quotation, tasks); - //Make the calculations for the tasks - for(Task t : tasks) { - TaskCalculation tc = new TaskCalculation(t, client); - tc.setSrp(config.getDailyReturn()); - tc.calculate(); - taskCalc.put(t, tc); - } - taskMap.put(quotation, tasks); - alertsMap.putAll(getAlerts(client, quotation)); - QuotationCalculation calc = new QuotationCalculation(quotation, client); - calc.setSrp(config.getDailyReturn()); - calc.calculate(); - calculations.put(quotation, calc); - - + List<QuotationData> q = new ArrayList<QuotationData>(); + q.add(new QuotationData(quotation, client)); + projectData.put(projectResult.get(0), q); - return renderView("dashboardSingleProject.jsp", "locale", client.getUserLocale(), - "title", "Tableau de bord projet", "projects", projectResult.getAll(), - "quotations", quotations, "taskMap", taskMap, "extensions", - Extensions.extensions, "alerts", alertsMap, "calculations", calculations, - "taskCalc", taskCalc); + return renderView("dashboardSingleProject.jsp", + "locale", client.getUserLocale(), + "title", "Tableau de bord projet", + "projects", projectData); } else { @@ -115,16 +98,13 @@ * @return */ public Render projectFilter(ChoremClient client, String id, String quotationFilter) { - + WikittyQueryResult<Project> projectResult = null; WikittyQueryResult<Quotation> quotationResult = null; - WikittyQueryResult<Task> taskResult = null; - HashMap<Project, List<Quotation>> quotations = new HashMap<Project, List<Quotation>>(); - HashMap<Quotation, List<Task>> taskMap = new HashMap<Quotation, List<Task>>(); - HashMap<Task, String> alertsMap = new HashMap<Task, String>(); - HashMap<Quotation, QuotationCalculation> calculations = new HashMap<Quotation, QuotationCalculation>(); - HashMap<Task, TaskCalculation> taskCalc = new HashMap<Task, TaskCalculation>(); + + HashMap<Project, List<QuotationData>> projectData = new HashMap<Project, List<QuotationData>>(); + //Fetch the projects from the id or the filter WikittyQueryMaker projectQueryMaker = new WikittyQueryMaker(); if(id != null && !id.equals("")) @@ -134,10 +114,6 @@ WikittyQuery projectQuery = projectQueryMaker.end(); projectResult = client.findAllByQuery(Project.class, projectQuery); - - Configuration config = client.findAllByQuery(Configuration.class, - (new WikittyQueryMaker()).exteq(Configuration.EXT_CONFIGURATION).end() - ).get(0); //Fetch the quotations from the projects if(projectResult.size() != 0) { @@ -160,100 +136,28 @@ quotationQuery.setLimit(20); quotationQuery.addSortDescending(Quotation.ELEMENT_FIELD_INTERVAL_BEGINDATE); quotationResult = client.findAllByQuery(Quotation.class, quotationQuery); - quotations.put(project, quotationResult.getAll()); //Fetch the tasks form the quotations (if there are some) - if(quotationResult != null) { + if(quotationResult != null && quotationResult.size() != 0) { + List<QuotationData> quotation = new ArrayList<QuotationData>(); for(Quotation quote : quotationResult.getAll()) { - List<Task> tasks = fetchTasks(quote, client); - taskMap.put(quote, tasks); - for(Task t : tasks) { - TaskCalculation tc = new TaskCalculation(t, client); - tc.setSrp(config.getDailyReturn()); - tc.calculate(); - taskCalc.put(t, tc); - } - - alertsMap.putAll(getAlerts(client, quote)); - - QuotationCalculation calc = new QuotationCalculation(quote, client); - calc.setSrp(config.getDailyReturn()); - calc.calculate(); - calculations.put(quote, calc); + quotation.add(new QuotationData(quote, client)); } + projectData.put(project, quotation); } } } - return renderView("dashboardSingleProject.jsp", "locale", client.getUserLocale(), - "title", "Tableau de bord projet", "projects", projectResult.getAll(), - "quotations", quotations, "taskMap", taskMap, "extensions", - Extensions.extensions, "alerts", alertsMap, "calculations", calculations, - "taskCalc", taskCalc); + return renderView("dashboardSingleProject.jsp", + "locale", client.getUserLocale(), + "title", "Tableau de bord projet", + "projects", projectData); } - /** - * Fetch the task from the given quotation - * Simple wikitty query - * @param q Quotation - * @param client chorem client - * @return list of task from the quotation - */ - private List<Task> fetchTasks(Quotation q, ChoremClient client) { - WikittyQuery taskQuery = new WikittyQueryMaker() - .eq(Task.ELEMENT_FIELD_TASK_QUOTATION, q) - .end(); - - taskQuery.addSortAscending(Quotation.ELEMENT_FIELD_INTERVAL_BEGINDATE); - WikittyQueryResult taskResult = client.findAllByQuery(Task.class, taskQuery); - return taskResult.getAll(); - } + - private HashMap<Task, String> getAlerts(ChoremClient client, Quotation q) { - List<Task> tasks = fetchTasks(q, client); - Calendar now = new GregorianCalendar(); - HashMap<Task, String> messages = new HashMap<Task, String>(); - for(Task t : tasks) { - if(t.getBeginDate() != null && t.getEndDate() != null) { - String str = "<h4>Warning</h4>"; - boolean alert = false; - //Test if the statuses are correct - if(t.getStatus().equalsIgnoreCase("Scheduled")) { - if(t.getBeginDate().before(now.getTime())) { - alert = true; - str += "Task " + t.getName() + " should be started"; - } - } - else if(t.getStatus().equalsIgnoreCase("Started")) { - if(t.getBeginDate().after(now.getTime())) { - alert = true; - str += "Task " + t.getName() + " has been started in advance"; - } - else if(t.getEndDate().before(now.getTime())) { - alert = true; - str += "Task " + t.getName() + " should have ended by now"; - } - } - else if(t.getStatus().equalsIgnoreCase("Finished")) { - - if(t.getEndDate().after(now.getTime())) { - alert = true; - str += "Task " + t.getName() + " has been finished in advance"; - } - } - if(alert) { - messages.put(t,str); - } - - } - - } - - return messages; - } - public Render multiProjectFilter(ChoremClient client, Date from, Date to) { System.out.println(from + "," + to); if(from == null || to == null) { @@ -267,12 +171,7 @@ } - Configuration config = client.findAllByQuery(Configuration.class, - (new WikittyQueryMaker()).exteq(Configuration.EXT_CONFIGURATION).end() - ).get(0); - - WikittyQueryMaker quotationQueryMaker = new WikittyQueryMaker(); WikittyQuery quotationQuery = quotationQueryMaker.or() .bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from, to) @@ -289,7 +188,6 @@ HashMap<Quotation, QuotationCalculation> calculations = new HashMap<Quotation, QuotationCalculation>(); for(Quotation q : quotations) { QuotationCalculation calc = new QuotationCalculation(q, client); - calc.setSrp(config.getDailyReturn()); calc.calculate(); calculations.put(q, calc); } @@ -304,163 +202,149 @@ } - - - public Render requestProject(ChoremClient client, String project_name, String project_id, String quotationFilter) { - if(quotationFilter == null) - quotationFilter = "open"; - if(project_name == null || project_name.equals("")) - project_id = ""; - if(quotationFilter.equals("open") || quotationFilter.equals("all")) { - return projectFilter(client, project_id, quotationFilter); + public Render employeeFilter(ChoremClient client, String id, Date from, Date to) { + + //If no field has been set, sets the date to the actual month + if(from == null || to == null) { + Calendar now = new GregorianCalendar(); + Calendar gFrom = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH) + , now.getActualMinimum(Calendar.DAY_OF_MONTH)); + Calendar gTo = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH) + , now.getActualMaximum(Calendar.DAY_OF_MONTH)); + from = gFrom.getTime(); + to = gTo.getTime(); } + + //Fetch the total list of employee (or just the chosen one + WikittyQueryMaker employeeQueryMaker = new WikittyQueryMaker(); + if(id != null && !id.equals("")) + employeeQueryMaker.ideq(id); else { - return singleQuotationFilter(client, project_id, quotationFilter); + employeeQueryMaker.exteq("Employee"); } - } - public Render requestMultiProject(ChoremClient client, Date from, Date to) { - return multiProjectFilter(client, from, to); - } - - public Render getGanttInfo(ChoremClient client, String id) { - Wikitty wikitty = client.restore(id); - WikittyQuery quotationQuery = new WikittyQueryMaker() - .ideq(id) - .end(); - Quotation quotation = client.findByQuery(Quotation.class, quotationQuery); - - - + WikittyQueryResult<Employee> employeeResult = client.findAllByQuery(Employee.class,employeeQueryMaker.end()); + List<Employee> employeeList = employeeResult.getAll(); + + //Fetch all the tasks on the interval WikittyQuery taskQuery = new WikittyQueryMaker() - .eq(Task.ELEMENT_FIELD_TASK_QUOTATION, quotation) - .end(); - - WikittyQueryResult<Task> taskResult = client.findAllByQuery(Task.class, taskQuery); - String customClass = ""; - List<JTask> lTask = new ArrayList<JTask>(); - for(Task t : taskResult.getAll()) { - - if(t.getBeginDate() != null && t.getEndDate() != null) { - if(t.getStatus().equals("SCHEDULED")) { - customClass = "ganttBlue"; + .and() + .exteq("Task") + .or() + .bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from, to) + .bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, from, to) + .and() + .le(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from) + .ge(Interval.FQ_FIELD_INTERVAL_ENDDATE, to) + .end(); + List<Task> taskList = client.findAllByQuery(Task.class,taskQuery).getAll(); + //Fetch the employee's workers on those tasks + + + + //---------------------------------------- + //Fetch the real time spent on each project/quotation + HashMap<Employee, Double> timeTotals = new HashMap<Employee, Double>(); + HashMap<Employee, HashMap<Quotation, Double>> timePerQuotation = new HashMap<Employee, HashMap<Quotation, Double>>(); + for(Task task : taskList) { + + WikittyQueryMaker timeQueryMaker = new WikittyQueryMaker().and() + .eq(Time.ELEMENT_FIELD_TIME_TASK, task); + if(employeeList.size() == 1) + timeQueryMaker.eq(Time.ELEMENT_FIELD_TIME_EMPLOYEE, employeeList.get(0)); + WikittyQuery timeQuery = timeQueryMaker.end(); + WikittyQueryResult<Time> timeResult = client.findAllByQuery(Time.class, timeQuery); + + + + for(Time time : timeResult.getAll()) { + Employee emp = time.getEmployee(false); + + //TOTAL TIMES + if(timeTotals.containsKey(emp)) + timeTotals.put(emp, timeTotals.get(emp) + ChoremUtil.getPeriodInHours(time.getBeginDate(), time.getEndDate())); + else + timeTotals.put(emp, ChoremUtil.getPeriodInHours(time.getBeginDate(), time.getEndDate())); + + HashMap<Quotation, Double> timeQ = null; + //TIME PER QUOTATION + if(timePerQuotation.containsKey(emp)) { + timeQ = timePerQuotation.get(emp); } - else if(t.getStatus().equals("STARTED")) { - customClass = "ganttGreen"; + else { + timeQ = new HashMap<Quotation, Double>(); + timePerQuotation.put(emp, timeQ); } - else if(t.getStatus().equals("FINISHED")) { - customClass = "ganttRed"; + Quotation qKey = task.getQuotation(false); + if(timeQ.containsKey(qKey)) { + timeQ.put(qKey, timeQ.get(qKey) + + ChoremUtil.getPeriodInHours(time.getBeginDate(), time.getEndDate())); } - else if(t.getStatus().equals("CLOSED")) { - customClass = "ganttGrey"; + else { + timeQ.put(qKey, + ChoremUtil.getPeriodInHours(time.getBeginDate(), time.getEndDate())); } - - Values[] v = null; - if(t.getDayExtension() != 0) - v= new Values[2]; - else - v = new Values[1]; - v[0] = new Values(t.getBeginDate(), t.getEndDate(), t.getName(), customClass, t.getWikittyId()); - if(t.getDayExtension() != 0) { - GregorianCalendar newEnd = new GregorianCalendar(); - newEnd.setTime(t.getEndDate()); - newEnd.add(Calendar.DAY_OF_YEAR, (int) t.getDayExtension()); - v[1]= new Values(t.getEndDate(), newEnd.getTime(), "Reestimated end", "ganttOrange", t.getWikittyId()); - } - JTask jt = new JTask(t.getName(), t.getDescription(), t.getWikittyId(),t.getPrice(), t.getEstimatedDays(), v); - - lTask.add(jt); + } } - - Collections.sort(lTask); - String dateStart = "/Date(" + quotation.getBeginDate().getTime() + ")/"; - String dateEnd = "/Date(" + quotation.getEndDate().getTime() + ")/"; - - HashMap<String,String> dateMap = new HashMap<String,String>(); - dateMap.put("sendingDate", "Draft"); - dateMap.put("postedDate", "Sent"); - dateMap.put("acceptedDate", "Accepted"); - dateMap.put("startedDate", "Started"); - dateMap.put("deliveryDate", "Delivered"); - dateMap.put("rsvStart", "RSV"); - dateMap.put("warrantyStart", "Warranty"); - dateMap.put("closedDate", "Closed"); - dateMap.put("rejectedDate", "Rejected"); - dateMap.put("cancelledDate", "Cancelled"); - - Iterator i = dateMap.entrySet().iterator(); - HashMap<String,String> extDate = new HashMap<String,String>(); - while(i.hasNext()) { - Map.Entry<String, String> me = (Map.Entry<String, String>)i.next(); - if(wikitty.hasExtension(me.getValue())) { - Date date = ((Date) (wikitty.getFieldAsObject(me.getValue(), me.getKey()))); - if(date != null) - extDate.put(me.getValue() + " " + me.getKey(), "/Date(" + date.getTime() +")/"); + Set<Employee> kSet = timePerQuotation.keySet(); + Set<Quotation> quotations = new HashSet<Quotation>(); + for(Employee e : kSet) { + quotations.addAll(timePerQuotation.get(e).keySet()); + } + + HashMap<Employee, HashMap<Quotation, Double>> percentages = new HashMap<Employee, HashMap<Quotation, Double>>(); + + for(Employee e : kSet) { + Set<Quotation> qSet = timePerQuotation.get(e).keySet(); + HashMap<Quotation, Double> p = new HashMap<Quotation, Double>(); + for(Quotation q : qSet) { + + p.put(q, ( timePerQuotation.get(e).get(q) / timeTotals.get(e) ) *100 ); + } + percentages.put(e, p); } - - JRender data = new JRender(lTask, extDate, dateStart, dateEnd); - return renderJSON("data", data); + + //Calculates the average working time + + Calendar gFrom = new GregorianCalendar(); + Calendar gTo = new GregorianCalendar(); + gFrom.setTime(from); + gTo.setTime(to); + + return renderView("dashboardEmployee.jsp", + "title", "Tableau de bord employé", + "locale", client.getUserLocale(), + "timePerQuotation", timePerQuotation, + "quotations", quotations, + "percentages", percentages, + "timeTotals", timeTotals); + } - private class JRender { - private List<JTask> source; - private String dateStart; - private String dateEnd; - private HashMap<String,String> extDate; - public JRender(List<JTask> source,HashMap<String,String> extDate,String dateStart,String dateEnd) { - this.source = source; - this.dateStart = dateStart; - this.dateEnd = dateEnd; - this.extDate = extDate; + public Render requestProject(ChoremClient client, String project_name, String project_id, String quotationFilter) { + if(quotationFilter == null) + quotationFilter = "open"; + if(project_name == null || project_name.equals("")) + project_id = ""; + if(quotationFilter.equals("open") || quotationFilter.equals("all")) { + return projectFilter(client, project_id, quotationFilter); } - } - - private class JTask implements Comparable { - private String name; - private String desc; - private Values[] values; - private String status; - private String wikittyId; - private float price; - private double estimatedDays; - public JTask(String name, String desc, String wikittyId,float price, double estimatedDays, Values[] values) { - this.name = name; - this.desc = desc; - this.values = values; - this.wikittyId = wikittyId; - this.price = price; - this.estimatedDays = estimatedDays; + else { + return singleQuotationFilter(client, project_id, quotationFilter); } - @Override - public int compareTo(Object task) { - - return values[0].compareTo(((JTask)task).values[0]); - } - } - private class Values implements Comparable{ - private String from; - private String to; - private String label; - private String customClass; - private String dataObj; - public Values(Date from, Date to, String label, String customClass, String dataObj) { - this.from = "/Date(" + from.getTime() + ")/"; - this.to = "/Date(" + to.getTime() + ")/"; - this.label = label; - this.customClass = customClass; - this.dataObj = dataObj; - } - @Override - public int compareTo(Object o) { - - return from.compareTo(((Values)o).from); - } - + public Render requestMultiProject(ChoremClient client, Date from, Date to) { + return multiProjectFilter(client, from, to); } + + public Render requestEmployee(ChoremClient client, String id, Date from, Date to) { + + return employeeFilter(client, id, from, to); + } + private class IntervalSorter<T extends Interval> implements Comparator { @Override Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java 2013-07-17 12:11:46 UTC (rev 365) @@ -0,0 +1,169 @@ +package org.chorem.webmotion.actions.project; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.ChoremClient; +import org.chorem.entities.Quotation; +import org.chorem.entities.Task; +import org.debux.webmotion.server.WebMotionController; +import org.debux.webmotion.server.render.Render; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryMaker; +import org.nuiton.wikitty.query.WikittyQueryResult; + +public class GanttAction extends WebMotionController { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(GanttAction.class); + + + public Render getGanttInfo(ChoremClient client, String id) { + Wikitty wikitty = client.restore(id); + WikittyQuery quotationQuery = new WikittyQueryMaker() + .ideq(id) + .end(); + Quotation quotation = client.findByQuery(Quotation.class, quotationQuery); + + + + WikittyQuery taskQuery = new WikittyQueryMaker() + .eq(Task.ELEMENT_FIELD_TASK_QUOTATION, quotation) + .end(); + + WikittyQueryResult<Task> taskResult = client.findAllByQuery(Task.class, taskQuery); + String customClass = ""; + List<JTask> lTask = new ArrayList<JTask>(); + for(Task t : taskResult.getAll()) { + + if(t.getBeginDate() != null && t.getEndDate() != null) { + if(t.getStatus().equals("SCHEDULED")) { + customClass = "ganttBlue"; + } + else if(t.getStatus().equals("STARTED")) { + customClass = "ganttGreen"; + } + else if(t.getStatus().equals("FINISHED")) { + customClass = "ganttRed"; + } + else if(t.getStatus().equals("CLOSED")) { + customClass = "ganttGrey"; + } + + Values[] v = null; + if(t.getDayExtension() != 0) + v= new Values[2]; + else + v = new Values[1]; + v[0] = new Values(t.getBeginDate(), t.getEndDate(), t.getName(), customClass, t.getWikittyId()); + if(t.getDayExtension() != 0) { + GregorianCalendar newEnd = new GregorianCalendar(); + newEnd.setTime(t.getEndDate()); + newEnd.add(Calendar.DAY_OF_YEAR, (int) t.getDayExtension()); + v[1]= new Values(t.getEndDate(), newEnd.getTime(), "Reestimated end", "ganttOrange", t.getWikittyId()); + } + JTask jt = new JTask(t.getName(), t.getDescription(), t.getWikittyId(),t.getPrice(), t.getEstimatedDays(), v); + + lTask.add(jt); + } + } + + Collections.sort(lTask); + String dateStart = "/Date(" + quotation.getBeginDate().getTime() + ")/"; + String dateEnd = "/Date(" + quotation.getEndDate().getTime() + ")/"; + + HashMap<String,String> dateMap = new HashMap<String,String>(); + dateMap.put("sendingDate", "Draft"); + dateMap.put("postedDate", "Sent"); + dateMap.put("acceptedDate", "Accepted"); + dateMap.put("startedDate", "Started"); + dateMap.put("deliveryDate", "Delivered"); + dateMap.put("rsvStart", "RSV"); + dateMap.put("warrantyStart", "Warranty"); + dateMap.put("closedDate", "Closed"); + dateMap.put("rejectedDate", "Rejected"); + dateMap.put("cancelledDate", "Cancelled"); + + Iterator<Map.Entry<String,String>> i = dateMap.entrySet().iterator(); + HashMap<String,String> extDate = new HashMap<String,String>(); + while(i.hasNext()) { + Map.Entry<String, String> me = (Map.Entry<String, String>)i.next(); + if(wikitty.hasExtension(me.getValue())) { + Date date = ((Date) (wikitty.getFieldAsObject(me.getValue(), me.getKey()))); + if(date != null) + extDate.put(me.getValue() + " " + me.getKey(), "/Date(" + date.getTime() +")/"); + } + } + + JRender data = new JRender(lTask, extDate, dateStart, dateEnd); + return renderJSON("data", data); + } + + + private class JRender { + private List<JTask> source; + private String dateStart; + private String dateEnd; + private HashMap<String,String> extDate; + public JRender(List<JTask> source,HashMap<String,String> extDate,String dateStart,String dateEnd) { + this.source = source; + this.dateStart = dateStart; + this.dateEnd = dateEnd; + this.extDate = extDate; + } + } + + private class JTask implements Comparable { + private String name; + private String desc; + private Values[] values; + private String status; + private String wikittyId; + private float price; + private double estimatedDays; + public JTask(String name, String desc, String wikittyId,float price, double estimatedDays, Values[] values) { + this.name = name; + this.desc = desc; + this.values = values; + this.wikittyId = wikittyId; + this.price = price; + this.estimatedDays = estimatedDays; + } + @Override + public int compareTo(Object task) { + + return values[0].compareTo(((JTask)task).values[0]); + } + + } + private class Values implements Comparable{ + private String from; + private String to; + private String label; + private String customClass; + private String dataObj; + public Values(Date from, Date to, String label, String customClass, String dataObj) { + this.from = "/Date(" + from.getTime() + ")/"; + this.to = "/Date(" + to.getTime() + ")/"; + this.label = label; + this.customClass = customClass; + this.dataObj = dataObj; + } + @Override + public int compareTo(Object o) { + + return from.compareTo(((Values)o).from); + } + + } +} Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java 2013-07-16 15:41:06 UTC (rev 364) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java 2013-07-17 12:11:46 UTC (rev 365) @@ -11,6 +11,8 @@ public class QuotationCalculation extends Calculation<Quotation> { + private List<Task> tasks = null; + public QuotationCalculation(Quotation q, ChoremClient client) { super(q, q.getAmount(), q.getEstimatedDays(), client); } @@ -30,7 +32,7 @@ @Override public HashMap<Employee, Double> getPercentages() { HashMap<Employee, Double> percentages = new HashMap<Employee, Double>(); - List<Task> tasks = client.getTasks(e); + List<Task> tasks = getTasks(); for(Task t : tasks) { HashMap<Employee, Double> taskPercentages = new TaskCalculation(t, client).getPercentages(); @@ -59,7 +61,7 @@ public HashMap<Employee, Double> getTimes() { HashMap<Employee, Double> times = new HashMap<Employee, Double>(); - for(Task t : client.getTasks(e)) { + for(Task t : getTasks()) { HashMap<Employee, Double> taskPercentages = new TaskCalculation(t, client).getTimes(); for(Employee emp : taskPercentages.keySet()) { @@ -76,4 +78,11 @@ return times; } + + public List<Task> getTasks() { + if(tasks == null) + tasks = client.getTasks(e); + return tasks; + + } } Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationData.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationData.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationData.java 2013-07-17 12:11:46 UTC (rev 365) @@ -0,0 +1,85 @@ +package org.chorem.webmotion.actions.project; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; + +import org.chorem.ChoremClient; +import org.chorem.entities.Task; +import org.chorem.entities.Quotation; + +public class QuotationData extends QuotationCalculation { + + + private List<TaskData> tasksData; + + public QuotationData(Quotation q, ChoremClient client) { + super(q, client); + tasksData = new ArrayList<TaskData>(); + + for(Task t : getTasks()) { + tasksData.add(new TaskData(t, client)); + } + this.calculate(); + getAlerts(); + } + + private void getAlerts() { + Calendar now = new GregorianCalendar(); + for(TaskData td : tasksData) { + Task t = td.getObject(); + if(t.getBeginDate() != null && t.getEndDate() != null) { + String str = "<h4>Warning</h4>"; + boolean alert = false; + //Test if the statuses are correct + if(t.getStatus().equalsIgnoreCase("Scheduled")) { + if(t.getBeginDate().before(now.getTime())) { + alert = true; + str += "Task " + t.getName() + " should be started"; + } + } + else if(t.getStatus().equalsIgnoreCase("Started")) { + if(t.getBeginDate().after(now.getTime())) { + alert = true; + str += "Task " + t.getName() + " has been started in advance"; + } + else if(t.getEndDate().before(now.getTime())) { + alert = true; + str += "Task " + t.getName() + " should have ended by now"; + } + } + else if(t.getStatus().equalsIgnoreCase("Finished")) { + + if(t.getEndDate().after(now.getTime())) { + alert = true; + str += "Task " + t.getName() + " has been finished in advance"; + } + } + if(alert) { + td.setAlert(str); + } + + } + + } + + } + + public TaskData getTaskData(Task t) { + for(TaskData td : tasksData) { + if(td.e == t) { + return td; + } + } + return null; + } + + + + public List<TaskData> getTasksData() { + return tasksData; + } + + +} Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/TaskData.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/TaskData.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/TaskData.java 2013-07-17 12:11:46 UTC (rev 365) @@ -0,0 +1,26 @@ +package org.chorem.webmotion.actions.project; + +import java.util.List; + +import org.chorem.ChoremClient; +import org.chorem.entities.Task; +import org.chorem.entities.Quotation; + +public class TaskData extends TaskCalculation { + + + private String alert; + + public TaskData(Task t, ChoremClient client) { + super(t, client); + this.calculate(); + } + + public void setAlert(String a) { + alert = a; + } + + public String getAlert() { + return alert; + } +} Modified: trunk/chorem-webmotion/src/main/resources/mapping =================================================================== --- trunk/chorem-webmotion/src/main/resources/mapping 2013-07-16 15:41:06 UTC (rev 364) +++ trunk/chorem-webmotion/src/main/resources/mapping 2013-07-17 12:11:46 UTC (rev 365) @@ -73,6 +73,7 @@ * /project action:project.DashboardProjectAction.requestProject * /project/multi action:project.DashboardProjectAction.requestMultiProject * /project/json/getExtension/{extensionName}/{id} action:project.QuotationStatusAction.getExtension -* /project/json/getGanttInfo/{id} action:project.DashboardProjectAction.getGanttInfo +* /project/json/getGanttInfo/{id} action:project.GanttAction.getGanttInfo +* /project/employee action:project.DashboardProjectAction.requestEmployee * /crm/account/{id} action:crm.AccountAction.view * /crm/quotation/edit/{id} action:crm.QuotationAction.edit Added: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp 2013-07-17 12:11:46 UTC (rev 365) @@ -0,0 +1,88 @@ +<%-- + #%L + Chorem webmotion + $Id:$ + $HeadURL:$ + %% + Copyright (C) 2011 - 2012 CodeLutin + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + #L% + --%> +<%@page contentType="text/html" pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="f"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> +<%@ taglib uri="/WEB-INF/wikitty.tld" prefix="w"%> + +<f:setLocale value="${locale}"/> + + + +<h1>${title}</h1> +<form class="well form-inline" method="GET" id="projectSearch"> + + <div class="control-group"> + <div class="controls" style="display: inline"> + + From : <input type="text" name="from" id="from" class="datepicker" value='${param.from}' /> + to : <input type="text" name="to" id="to" class="datepicker" value="${param.to}" /> + <select class="filterBox" name="id" id="employeeFilter"> + <option value="">Tous les employés</option> + <c:forEach items="${timeTotals}" var="entry"> + <option value="${entry.key.wikittyId}"><w:display wikitty="${entry.key.wikitty}" + fqfield="Employee.person" label="" /></option> + </c:forEach> + </select> + <input type="submit"/> + </div> + </div> + +</form> + +<table class="table table-striped table-bordered table-condensed"> + <thead> + <th>Employé</th> + <c:forEach items="${quotations}" var="q"> + <th>${q.getProject(false).name} :<a href="<c:url value="/project?quotationFilter=${q.wikittyId}" />"> ${q.description}</a></th> + </c:forEach> + <th>Total</th> + </thead> + <tbody> + <c:forEach items="${timePerQuotation}" var="entry"> + <tr> + <td><w:display wikitty="${entry.key.wikitty}" + fqfield="Employee.person" label="" /></td> + <c:forEach items="${quotations}" var="q"> + <td> + <c:if test="${not empty entry.value[q]}"> + <f:formatNumber type="number" + maxFractionDigits="2" value="${percentages[entry.key][q]}" /> % + (<f:formatNumber type="number" + maxFractionDigits="2" value="${entry.value[q]}" /> heures) + </c:if> + </td> + + + </c:forEach> + <td><f:formatNumber type="number" + maxFractionDigits="2" value="${timeTotals[entry.key]}" /> heures</td> + </tr> + + </c:forEach> + </tbody> +</table> + + + Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp 2013-07-16 15:41:06 UTC (rev 364) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp 2013-07-17 12:11:46 UTC (rev 365) @@ -56,6 +56,8 @@ <th><abbr title="Taux journalier moyen">TJM</abbr> reel</th> <th>Gain attendu</th> <th>Perte/gain</th> + <th>Average SRP</th> + <th>Real SRP</th> </tr> </thead> <c:forEach var="q" items="${quotations}"> @@ -82,6 +84,10 @@ maxFractionDigits="2" value="${calculations[q].getExpectedProfit()}" /></td> <td class="currency"> <f:formatNumber type="currency" maxFractionDigits="2" value="${calculations[q].getLossOrProfit()}" /></td> + <td> <f:formatNumber type="number" + maxFractionDigits="2" value="${calculations[q].getAvgSrp()}" /></td> + <td> <f:formatNumber type="number" + maxFractionDigits="2" value="${calculations[q].getRealSrp()}" /></td> </tr> </tbody> Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-16 15:41:06 UTC (rev 364) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-17 12:11:46 UTC (rev 365) @@ -41,11 +41,11 @@ <option value="all" <c:if test='${param.quotationFilter=="all"}'>selected</c:if>>Tous les devis</option> - <c:forEach var="p" items="${projects}"> - <c:forEach var="q" items="${quotations[p]}"> - <option value="${q.wikittyId}" - <c:if test='${param.quotationFilter.equals(q.wikittyId)}'>selected</c:if>>${p.name} - - ${q.description}</option> + <c:forEach var="entry" items="${projects}"> + <c:forEach var="q" items="${entry.value}"> + <option value="${q.object.wikittyId}" + <c:if test='${param.quotationFilter.equals(q.object.wikittyId)}'>selected</c:if>>${entry.key.name} + - ${q.object.description}</option> </c:forEach> </c:forEach> </select> @@ -78,8 +78,8 @@ </form> -<c:forEach var="p" items="${projects}"> - <c:if test="${not empty quotations[p]}"> +<c:forEach var="entry" items="${projects}"> + <c:set var="p" value="${entry.key}"/> <h2 style="display: inline;">${p.name}</h2> <a href="<c:url value="/wikitty/edit/${p.wikittyId}"/>"><i class="icon-pencil icon-black"></i><small>edit</small></a> @@ -88,23 +88,23 @@ - <c:forEach var="q" items="${quotations[p]}"> + <c:forEach var="q" items="${entry.value}"> <h3 style="display: inline;"> - <w:display wikitty="${q.wikitty}" fqfield="Quotation.description" + <w:display wikitty="${q.object.wikitty}" fqfield="Quotation.description" label="" /> </h3> - <a href="<c:url value="/wikitty/edit/${q.wikittyId}"/>"><i + <a href="<c:url value="/wikitty/edit/${q.object.wikittyId}"/>"><i class="icon-pencil icon-black"></i><small>edit</small></a> <p> Du - <w:display wikitty="${q.wikitty}" fqfield="Interval.beginDate" + <w:display wikitty="${q.object.wikitty}" fqfield="Interval.beginDate" label="" pattern="dd/MM/yyyy" /> au - <w:display wikitty="${q.wikitty}" fqfield="Interval.endDate" + <w:display wikitty="${q.object.wikitty}" fqfield="Interval.endDate" label="" pattern="dd/MM/yyyy" /> <br />Statut : - ${q.wikitty.extensionNames.toArray()[q.wikitty.extensionNames.size() + ${q.object.wikitty.extensionNames.toArray()[q.object.wikitty.extensionNames.size() -1]} </p> @@ -115,6 +115,7 @@ <th>Responsable Code Lutin</th> <th>Responsable entreprise</th> <th>Jours estimés</th> + <th>Jours réels</th> <th>TJM estimé</th> <th>TJM réel</th> <th>Montant</th> @@ -124,21 +125,23 @@ <tbody> <tr> - <td><w:display wikitty="${q.wikitty}" + <td><w:display wikitty="${q.object.wikitty}" fqfield="Quotation.supplier" label="" /></td> - <td><w:display wikitty="${q.wikitty}" + <td><w:display wikitty="${q.object.wikitty}" fqfield="Quotation.customer" label="" /></td> - <td class="number"><w:display wikitty="${q.wikitty}" + <td class="number"><w:display wikitty="${q.object.wikitty}" fqfield="Quotation.estimatedDays" label="" /></td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${q.getRealDays()}" /></td> <td class="currency"><f:formatNumber type="currency" - maxFractionDigits="2" value="${calculations[q].getAdr()}" /></td> + maxFractionDigits="2" value="${q.getAdr()}" /></td> <td class="currency"><f:formatNumber type="currency" - maxFractionDigits="2" value="${calculations[q].getRealAdr()}" /></td> - <td class="currency"><w:display wikitty="${q.wikitty}" + maxFractionDigits="2" value="${q.getRealAdr()}" /></td> + <td class="currency"><w:display wikitty="${q.object.wikitty}" fqfield="Quotation.amount" label="" /></td> <td class="currency"><f:formatNumber type="currency" maxFractionDigits="2" - value="${calculations[q].getLossOrProfit()}" /></td> + value="${q.getLossOrProfit()}" /></td> </tr> </tbody> </table> @@ -147,9 +150,9 @@ - <div class="gantt gantt-${q.wikittyId}" wikittyId="${q.wikittyId}" + <div class="gantt gantt-${q.object.wikittyId}" wikittyId="${q.object.wikittyId}" data=''>Pas de tâche</div> - <c:if test="${taskMap[q].size() != 0}"> + <c:if test="${q.tasks.size() != 0}"> <table class="table table-striped table-bordered table-condensed"> <thead> <tr> @@ -162,40 +165,46 @@ <th>TJM réel</th> <th>Gain attendu</th> <th>Gain/perte</th> + <th>SRP moyen</th> + <th>SRP réel</th> </tr> </thead> <tbody> - <c:forEach items="${taskMap[q]}" var="task"> - <tr id="taskrow-${task.wikittyId}"> - <td <c:if test="${not empty alerts[task]}">class="task-alert"</c:if>><a - href="<c:url value="/wikitty/view/${task.wikittyId}"/>"> <w:display - wikitty="${task.wikitty}" fqfield="Task.name" label="" /></a> - <c:if test="${not empty alerts[task]}"> - <i class="icon-warning-sign" title="${alerts[task]}"></i> + <c:forEach items="${q.tasksData}" var="task"> + <tr id="taskrow-${task.object.wikittyId}"> + <td <c:if test="${not empty task.alert}">class="task-alert"</c:if>><a + href="<c:url value="/wikitty/view/${task.object.wikittyId}"/>"> <w:display + wikitty="${task.object.wikitty}" fqfield="Task.name" label="" /></a> + <c:if test="${not empty task.alert}"> + <i class="icon-warning-sign" title="${task.alert}"></i> </c:if> - <td><w:display wikitty="${task.wikitty}" + <td><w:display wikitty="${task.object.wikitty}" fqfield="Task.price" label="" /></td> - <td class="number"><w:display wikitty="${task.wikitty}" + <td class="number"><w:display wikitty="${task.object.wikitty}" fqfield="Task.estimatedDays" label="" /></td> <td class="number"><f:formatNumber type="number" - maxFractionDigits="2" value="${taskCalc[task].getRealDays()}" /></td> + maxFractionDigits="2" value="${task.getRealDays()}" /></td> <td class="number"><f:formatNumber type="number" - maxFractionDigits="2" value="${taskCalc[task].getDeltaDays()}" /></td> + maxFractionDigits="2" value="${task.getDeltaDays()}" /></td> <td class="number"><f:formatNumber type="number" - maxFractionDigits="2" value="${taskCalc[task].getAdr()}" /></td> + maxFractionDigits="2" value="${task.getAdr()}" /></td> <td class="number"><f:formatNumber type="number" - maxFractionDigits="2" value="${taskCalc[task].getRealAdr()}" /></td> + maxFractionDigits="2" value="${task.getRealAdr()}" /></td> <td class="currency"><f:formatNumber type="currency" maxFractionDigits="2" - value="${taskCalc[task].getExpectedProfit()}" /></td> + value="${task.getExpectedProfit()}" /></td> <td class="currency"><f:formatNumber type="currency" maxFractionDigits="2" - value="${taskCalc[task].getLossOrProfit()}" /></td> + value="${task.getLossOrProfit()}" /></td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${task.getAvgSrp()}" /></td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${task.getRealSrp()}" /></td> </tr> @@ -203,9 +212,34 @@ </tbody> </table> </c:if> + + <table class="table table-striped table-bordered table-condensed"> + <thead> + <th>Employé</th> + <th>Pourcentage estimé</th> + <th>Temps passé réel</th> + </thead> + <tbody> + <c:forEach items="${q.getPercentages()}" var="entry"> + <tr> + <td>${entry.key}</td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${entry.value}"/>%</td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" + value="${q.getTimePercentages()[entry.key]}" />% + ( + <f:formatNumber type="number" + maxFractionDigits="2" + value="${q.getTimes()[entry.key]}" /> + heures )</td> + </tr> + </c:forEach> + </tbody> + </table> </c:forEach> - </c:if> + </c:forEach> Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp 2013-07-16 15:41:06 UTC (rev 364) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp 2013-07-17 12:11:46 UTC (rev 365) @@ -115,6 +115,7 @@ <li>Worker</li> <li><a href="<c:url value="/wikitty/Worker/search"/>"><i class="icon-th-list icon-black"></i> All workers</a></li> <li><a href="<c:url value="/wikitty/Worker/edit/new"/>"><i class="icon-plus icon-black"></i> Add worker</a></li> + <li><a href="<c:url value="/project/employee"/>"><i class="icon-plus icon-black"></i> Employee dashboard</a></li> <li class="divider"></li> <li>Time</li> <li><a href="<c:url value="/wikitty/Time/search"/>"><i class="icon-th-list icon-black"></i> All times</a></li>
participants (1)
-
meynier@users.chorem.org