Author: jcouteau Date: 2013-01-26 11:23:12 +0100 (Sat, 26 Jan 2013) New Revision: 305 Url: http://chorem.org/projects/chorem/repository/revisions/305 Log: Add a report on sales per account (customer) Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/AccountSalesReportAction.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/SalesPerAccountReportAction.java trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/accountSalesReport.jsp trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/salesPerAccountReport.jsp Modified: trunk/chorem-webmotion/src/main/resources/log4j.properties trunk/chorem-webmotion/src/main/resources/mapping trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/projectSalesReport.jsp Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/AccountSalesReportAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/AccountSalesReportAction.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/AccountSalesReportAction.java 2013-01-26 10:23:12 UTC (rev 305) @@ -0,0 +1,93 @@ +package org.chorem.webmotion.actions.sales; + +import org.chorem.ChoremClient; +import org.chorem.entities.Accepted; +import org.chorem.entities.Quotation; +import org.debux.webmotion.server.WebMotionController; +import org.debux.webmotion.server.render.Render; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryMaker; +import org.nuiton.wikitty.query.conditions.Aggregate; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * @author jcouteau <couteau@codelutin.com> + */ +public class AccountSalesReportAction extends WebMotionController { + + /** + * Rend le graphe des devis envoyés par mois + * + * @param client + * @return + */ + public Render sales(ChoremClient client, String account, String from, + String to) { + + if (null == from) { + from = String.valueOf(SalesReportHelper.getFirstYear(client)); + } + + if (null == to) { + to = String.valueOf(SalesReportHelper.getLastYear()); + } + + Map<Integer, SalesData> salesData = new LinkedHashMap<Integer, SalesData>(); + + List<Integer> listAllYears = SalesReportHelper.listAllYears(from, to); + + List<Integer> listAllYearsInChorem = SalesReportHelper.listAllYears(client); + + int previousYearValue = 0; + + for (Integer year:listAllYears){ + Date yearFirstDay = SalesReportHelper.getFirstDayOfYear(year); + Date yearLastDay = SalesReportHelper.getLastDayOfYear(year); + + SalesData yearData = new SalesData(); + + WikittyQuery projectQuery = new WikittyQueryMaker() + .select("Quotation.amount", Aggregate.SUM).and() + .eq(Quotation.FQ_FIELD_QUOTATION_CUSTOMER, account) + .bw(Accepted.FQ_FIELD_ACCEPTED_ACCEPTEDDATE, yearFirstDay, + yearLastDay) + .end(); + + Integer sales = client.findByQuery(Integer.class, projectQuery); + + //TODO JC 2012-01-26 Find a way to replace two queries into one. + WikittyQuery quotationsQuery = new WikittyQueryMaker().and() + .eq(Quotation.FQ_FIELD_QUOTATION_CUSTOMER, account) + .bw(Accepted.FQ_FIELD_ACCEPTED_ACCEPTEDDATE, yearFirstDay, + yearLastDay) + .end(); + + List<Quotation> quotations = client.findAllByQuery(Quotation.class, + quotationsQuery).getAll(); + + //Progression devis envoyés + int salesProgression = 0; + if (previousYearValue != 0){ + salesProgression = 100 * (sales - previousYearValue) / previousYearValue; + } + + previousYearValue = sales; + + yearData.setSales(sales); + yearData.setProgression(salesProgression); + yearData.setQuotations(quotations.size()); + + salesData.put(year, yearData); + } + + return renderView("salesReports/accountSalesReport.jsp", + "data", salesData, + "allYears", listAllYearsInChorem, + "fromYear", from, + "toYear", to); + } +} Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/SalesPerAccountReportAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/SalesPerAccountReportAction.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/sales/SalesPerAccountReportAction.java 2013-01-26 10:23:12 UTC (rev 305) @@ -0,0 +1,99 @@ +package org.chorem.webmotion.actions.sales; + +import org.chorem.ChoremClient; +import org.chorem.entities.Accepted; +import org.chorem.entities.Employee; +import org.chorem.entities.Quotation; +import org.debux.webmotion.server.WebMotionController; +import org.debux.webmotion.server.render.Render; +import org.nuiton.wikitty.WikittyClient; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryMaker; +import org.nuiton.wikitty.query.conditions.Aggregate; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * @author jcouteau <couteau@codelutin.com> + */ +public class SalesPerAccountReportAction extends WebMotionController { + + /** + * Rend le graphe des devis envoyés par mois + * + * @param client + * @return + */ + public Render sales(ChoremClient client, String from, String to) { + + if (null == from) { + from = String.valueOf(SalesReportHelper.getFirstYear(client)); + } + + if (null == to) { + to = String.valueOf(SalesReportHelper.getLastYear()); + } + + Map<Employee, SalesData> salesData = getSalesPerProjectData(from, to, client); + + List<Integer> listAllYearsInChorem = SalesReportHelper.listAllYears(client); + + return renderView("salesReports/salesPerAccountReport.jsp", + "data", salesData, + "allYears", listAllYearsInChorem, + "fromYear", from, + "toYear", to); + } + + protected Map<Employee,SalesData> getSalesPerProjectData(String firstYear, + String lastYear, + WikittyClient client){ + + Date last = SalesReportHelper.getLastDayOfYear(Integer.valueOf(lastYear)); + Date first = SalesReportHelper.getFirstDayOfYear(Integer.valueOf(firstYear)); + + Map<Employee,SalesData> salesData = new LinkedHashMap<Employee, SalesData>(); + + //a query to get all the accounts + //FIXME JC 2012-01-26 Really bad to find all employees and iterate on them :( + WikittyQuery accountsQuery = new WikittyQueryMaker().and() + .exteq(Employee.EXT_EMPLOYEE).end(); + + List<Employee> accounts = client.findAllByQuery(Employee.class, accountsQuery).getAll(); + + //iterate and two queries per account :( + for (Employee account:accounts){ + + SalesData projectData = new SalesData(); + + WikittyQuery accountQuery = new WikittyQueryMaker() + .select("Quotation.amount", Aggregate.SUM).and() + .eq(Quotation.FQ_FIELD_QUOTATION_CUSTOMER, account) + .bw(Accepted.FQ_FIELD_ACCEPTED_ACCEPTEDDATE, first, last) + .end(); + + Integer sales = client.findByQuery(Integer.class, accountQuery); + + //TODO JC 2012-01-26 Find a way to replace two queries into one. + WikittyQuery quotationsQuery = new WikittyQueryMaker().and() + .eq(Quotation.FQ_FIELD_QUOTATION_CUSTOMER, account) + .bw(Accepted.FQ_FIELD_ACCEPTED_ACCEPTEDDATE, first, last) + .end(); + + List<Quotation> quotations = client.findAllByQuery(Quotation.class, + quotationsQuery).getAll(); + + //Rempli la map que si on a des valeurs + if (null != sales && sales != 0) { + projectData.setSales(sales); + projectData.setQuotations(quotations.size()); + salesData.put(account, projectData); + } + } + + return salesData; + } +} Modified: trunk/chorem-webmotion/src/main/resources/log4j.properties =================================================================== --- trunk/chorem-webmotion/src/main/resources/log4j.properties 2013-01-23 15:33:00 UTC (rev 304) +++ trunk/chorem-webmotion/src/main/resources/log4j.properties 2013-01-26 10:23:12 UTC (rev 305) @@ -40,4 +40,5 @@ log4j.logger.org.chorem=DEBUG log4j.logger.org.nuiton=INFO log4j.logger.org.debux=ERROR +log4j.logger.org.apache=ERROR #log4j.logger.org.nuiton.wikitty.storage.solr.SolrUtil=DEBUG Modified: trunk/chorem-webmotion/src/main/resources/mapping =================================================================== --- trunk/chorem-webmotion/src/main/resources/mapping 2013-01-23 15:33:00 UTC (rev 304) +++ trunk/chorem-webmotion/src/main/resources/mapping 2013-01-26 10:23:12 UTC (rev 305) @@ -58,5 +58,5 @@ * /sales/report/sales action:sales.SalesReportAction.sales * /sales/report/salesPerProject action:sales.SalesPerProjectReportAction.sales * /sales/report/salesPerProject/{project} action:sales.ProjectSalesReportAction.sales -* /sales/{method} action:sales.SalesAction.{method} -* /sales/{method}/{id} action:sales.SalesAction.{method} +* /sales/report/salesPerAccount action:sales.SalesPerAccountReportAction.sales +* /sales/report/salesPerAccount/{account} action:sales.AccountSalesReportAction.sales Added: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/accountSalesReport.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/accountSalesReport.jsp (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/accountSalesReport.jsp 2013-01-26 10:23:12 UTC (rev 305) @@ -0,0 +1,122 @@ +<%-- + #%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="/WEB-INF/wikitty.tld" prefix="w"%> + +<div class="row-fluid"> + <div class="span2"> + <jsp:include page="/salesMenu"/> + </div> + + <div class="span10"> + <!-- Javascript to display graph --> + <script type="text/javascript"> + $(document).ready(function(){ + var sales = [ + <c:forEach var="entry" items="${data}" varStatus="counter"> + ['${entry.key}', ${entry.value.sales}] + <c:if test="${!counter.last}">, </c:if> + </c:forEach> + ]; + + var plot1 = $.jqplot ('sales', [sales], { + title:'Ventes par année', + seriesDefaults:{ + renderer:$.jqplot.BarRenderer, + rendererOptions: {fillToZero: true} + }, + axes:{ + xaxis:{ + pad: 0, + tickRenderer: $.jqplot.CanvasAxisTickRenderer , + tickOptions: { + angle: -30, + fontSize: '10pt' + }, + renderer: $.jqplot.CategoryAxisRenderer + }, + }, + highlighter: { + show: true, + tooltipAxes: 'y' + }, + cursor: { + show: false + }, + series:[ + <c:forEach var="entry" items="${data}" varStatus="counter"> + {label:${entry.key}}<c:if test="${!counter.last}">,</c:if> + </c:forEach>] + }); + + }); + </script> + + <h2>Ventes</h2> + + <form action="sales" method="get"> + <select name="from"> + <c:forEach var="entry" items="${allYears}" varStatus="counter"> + <option value="${entry}" <c:if test="${entry==fromYear}">selected</c:if>>${entry}</option> + </c:forEach> + </select> + <select name="to"> + <c:forEach var="entry" items="${allYears}" varStatus="counter"> + <option value="${entry}" ${entry == toYear ? 'selected' : ''} >${entry}</option> + </c:forEach> + </select> + <input type="submit" value="Rechercher"> + </form> + + <div id="sales" style="height:200px;"></div> + + + <table class="table table-striped table-bordered table-condensed"> + <thead> + <tr> + <th>Année</th> + <th>Ventes</th> + <th>Devis</th> + <th>€/devis</th> + <th>Progression</th> + </tr> + </thead> + <tbody> + <c:forEach var="year" items="${data}"> + <tr> + <td>${year.key}</td> + <td class="currency">${year.value.sales}</td> + <td>${year.value.quotations}</td> + <td class="currency">${year.value.mean}</td> + <td class="percent">${year.value.progression} %</td> + </tr> + </c:forEach> + </tbody> + </table> + + </div> +</div> + +<div style="clear:both;"/> \ No newline at end of file Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/projectSalesReport.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/projectSalesReport.jsp 2013-01-23 15:33:00 UTC (rev 304) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/projectSalesReport.jsp 2013-01-26 10:23:12 UTC (rev 305) @@ -68,8 +68,7 @@ series:[ <c:forEach var="entry" items="${data}" varStatus="counter"> {label:${entry.key}}<c:if test="${!counter.last}">,</c:if> - </c:forEach>], - legend: {show:true, location: 'e', placement: 'outsideGrid' } + </c:forEach>] }); }); Added: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/salesPerAccountReport.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/salesPerAccountReport.jsp (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/salesReports/salesPerAccountReport.jsp 2013-01-26 10:23:12 UTC (rev 305) @@ -0,0 +1,106 @@ +<%-- + #%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="/WEB-INF/wikitty.tld" prefix="w"%> + +<div class="row-fluid"> + <div class="span2"> + <jsp:include page="/salesMenu"/> + </div> + + <div class="span10"> + <!-- Javascript to display graph --> + <script type="text/javascript"> + $(document).ready(function(){ + var sales = [ + <c:forEach var="entry" items="${data}" varStatus="counter"> + ['${entry.key}', ${entry.value.sales}] + <c:if test="${!counter.last}">, </c:if> + </c:forEach> + ]; + + var plot1 = $.jqplot ('sales', [sales], { + title:'Ventes par compte client', + seriesDefaults:{ + renderer:$.jqplot.PieRenderer, + rendererOptions: { + showDataLabels: true, + dataLabels: 'value', + sliceMargin: 3 + } + }, + cursor: { + show: false + }, + legend: {show:true, location: 'e'} + }); + + }); + </script> + + <h2>Ventes</h2> + + <form action="sales" method="get"> + <select name="from"> + <c:forEach var="entry" items="${allYears}" varStatus="counter"> + <option value="${entry}" <c:if test="${entry==fromYear}">selected</c:if>>${entry}</option> + </c:forEach> + </select> + <select name="to"> + <c:forEach var="entry" items="${allYears}" varStatus="counter"> + <option value="${entry}" ${entry == toYear ? 'selected' : ''} >${entry}</option> + </c:forEach> + </select> + <input type="submit" value="Rechercher"> + </form> + + <div id="sales" style="height:200px;"></div> + + + <table class="table table-striped table-bordered table-condensed"> + <thead> + <tr> + <th>Compte client</th> + <th>Ventes</th> + <th>Devis</th> + <th>€/devis</th> + </tr> + </thead> + <tbody> + <c:forEach var="account" items="${data}"> + <tr> + <td><a href="<c:url value="/sales/report/salesPerAccount/${account.key.wikittyId}"/>">${account.key}</a></td> + <td>${account.value.sales}</td> + <td>${account.value.quotations}</td> + <td>${account.value.mean} €</td> + </tr> + </c:forEach> + </tbody> + </table> + + </div> +</div> + +<div style="clear:both;"/> \ No newline at end of file