diff --git a/AUTHORS b/AUTHORS index 32ea817b1..63b1888f0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -42,6 +42,7 @@ Translators Diego Pino García * [it] Giuseppe Zizza * [nl] Jeroen Baten +* [pl] Krzysztof Kamecki * [pt] Helena Grosso , Joaquim Rocha * [ru] Pavel Rudensky diff --git a/HACKING b/HACKING index 30aa2b9a4..64335ee0e 100644 --- a/HACKING +++ b/HACKING @@ -351,6 +351,44 @@ command. For example: mvn -Pprod,postgresql clean install +Compilation options +------------------- + +In LibrePlan there are two custom Maven properties, which allow you to configure +some small bits in the project. + +* *default.passwordsControl* - Warning about default passwords (``true`` by + default) + + If this option is enabled, a warning is show in LibrePlan footer to + application administrators in order to change the default password (which + matches with user login) for the users created by default: admin, user, + wsreader and wswriter. + +* *default.exampleUsersDisabled* - Disable default users (``true`` by default) + + If true, example default users such as user, wsreader and wswriter are + disabled. This is a good option for production environments. + + This option is set to ``false`` if you are using the development profile (the + default one). + +How to set compilation options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Maven properties have a default value, but you can change it using the parameter +``-D`` for Maven command to set the value of each option you want to modify. For +example: + +* Set *default.passwordsControl* to ``false``:: + + mvn -Ddefault.passwordsControl=false clean install + +* Set *default.passwordsControl* and *default.exampleUsersDisabled* to false:: + + mvn -Ddefault.passwordsControl=false -Ddefault.exampleUsersDisabled=false clean install + + Tests ----- diff --git a/INSTALL b/INSTALL index aa0b86ef2..4bfcaf94a 100644 --- a/INSTALL +++ b/INSTALL @@ -46,7 +46,7 @@ Instructions: * Download the package:: - $ wget http://downloads.sourceforge.net/project/libreplan/files/LibrePlan/libreplan_1.2.0-1_amd64.deb + $ wget http://downloads.sourceforge.net/project/libreplan/LibrePlan/libreplan_1.2.0-1_amd64.deb * Install package:: diff --git a/NEWS b/NEWS index d17ce1e90..f56670da8 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,179 @@ NEWS ==== +Version 1.2.3 (19 Apr 2012) +--------------------------- + +Summary +~~~~~~~ + +A new minor version of the LibrePlan 1.2.* version family. The main changes +included in this new release are: + +* Money based cost monitoring system: This is a new feature that allows users to + monitor the project cost based on the money spent comparing it to the budget. + Users can configure the budget for each task and, after this, LibrePlan + calculates the cost in money already spent using the worked time tracked, the + type of worked hours (standard, overtime,...) and the cost of each resource + hour according to the value defined by the cost category the worker belongs. + +* Polish language: LibrePlan is now translated into Polish thanks to the work + done by Krzysztof Kamecki. + +* Other minor enhancements and bugfixing: + + * Fixed database synchronization issues which appeared on changing planning + points in the WBS. The problems arose when planning points were moved from + children to their parents or vice versa. + * Default users (user, wsreader and wswriter) are disabled by default. + * Fixed resource usage grouped by criteria load analysis that was being bad + calculated inside a project. + * Task duration was not being refreshed properly when doing an allocation and + you needed to apply the allocation twice to see it right. + * START_IN_FIXED_DATE constraint caused that the project duration was bad + calculated in company view. + +Notes +~~~~~ + +.. WARNING:: + + Remove web browser cache to avoid any problem with changes in JavaScript + resources. + +If you are upgrading from any 1.1.x version without using the Debian package, +you will need to manually execute on your database the SQL sentences from files: +``scripts/database/upgrade_1.2.0.sql``, ``scripts/database/upgrade_1.2.1.sql``, +``scripts/database/upgrade_1.2.2.sql`` and ``scripts/database/upgrade_1.2.3.sql``. + +If you are upgrading from 1.2.0 version without using the Debian package, +you will need to manually execute on your database the SQL sentences from files: +``scripts/database/upgrade_1.2.1.sql``, ``scripts/database/upgrade_1.2.2.sql`` +and ``scripts/database/upgrade_1.2.3.sql``. + +If you are upgrading from 1.2.1 version without using the Debian package, +you will need to manually execute on your database the SQL sentences from files: +``scripts/database/upgrade_1.2.2.sql`` and ``scripts/database/upgrade_1.2.3.sql``. + +If you are upgrading from 1.2.2 version without using the Debian package, +you will need to manually execute on your database the SQL sentences from file: +``scripts/database/upgrade_1.2.3.sql``. + +Contributors +~~~~~~~~~~~~ + +Thanks to all the contributors to this new version: + +* Jacobo Aragunde Pérez +* Óscar González Fernández +* Susana Montes Pedreira +* Francisco Javier Morán Rúa +* Manuel Rego Casasnovas +* Lorenzo Tilve Álvaro + +Translators +~~~~~~~~~~~ + +Thanks to all the translators in this new version: + +* [es] Manuel Rego Casasnovas +* [fr] Philippe Poumaroux +* [gl] Manuel Rego Casasnovas +* [it] Giuseppe Zizza +* [nl] Jeroen Baten +* [pl] Krzysztof Kamecki + +Changes +~~~~~~~ + +* Bug #1417: Add UI validation to prevent empty codes +* doc: Update AUTHORS file info about new Polish translator +* i18n: Add Polish language to enum and modify pom.xml to use English userguide +* i18n: Add Polish translation +* Bug #1358: Removed unnecessary tooltip string +* Bug #1358: Added CSS max dimensions restriction to configured company logo +* Fixed vertical positioning of resources string next to containers +* Bug #1407: Run TaskComponent.updateProperties() after running the scheduling algorithm. +* Remove unused fields in Order Costs Per Resource Report +* Bug #1412: Fix problem converting to string clockStart and clockFinish +* Bug #1409: Rename RelatedWithAnyOf to RelatedWith +* Bug #1409: Fix problem replacing allocations for the ones related to the criterion +* Revert "Bug #1320: When asking a container for start constraints, return the leftmost" +* Bug #1411: Missing Spanish translation for "Project cost by resource" +* Fix Sahi test due to change in string +* i18n: Update Dutch translation +* i18n: Update French translation +* doc: Update HACKING file about the compilation options +* Add new compilation option to disable default users (user, wsreader and wswriter) +* Bug #1395: Fix issue setting width of date boxes to 100px +* i18n: Update Italian translation +* i18n: Update Spanish and Galician translations +* i18n: Update keys.pot files +* Bug #1402: Invalidate the TaskComponents instead of the whole GanttPanel +* Bug #1349: Fix translation issue in choosing template pop-up +* Bug #1349: Mark to translate exception day type +* Bug #1349: Fix translation in calendar type +* Bug #1298: Mark to translate roles in user and profile edition +* Remove CutyCaptTimeout +* Bug #1406: Add UI validation for name field too +* Bug #1406: Add validation in the UI and also a try catch for possible ValidationExceptions +* doc: Fix typo in INSTALL file +* doc: Fix broke link in INSTALL file +* doc: Update information about how to install in Fedora and openSUSE +* doc: Add info about JAVA_OPTS configuration in INSTALL file +* [Bug #1234] Fix the deletion of fields in progress reporting in subcontractor module. +* Fix problems in Liquibase changes in MySQL +* Fix compilation error in previous merge +* Merge branch 'libreplan-1.2' into money-cost-monitoring-system +* doc: Add info about add-apt-repository command in INSTALL file +* Bug #1387: Code refactor of the previous patches for this bug. +* Bug #1387: Fix bug when it happens in the opposite way. +* Show budget information in a read-only field inside task properties tab +* Bug #1387: Fix bug +* Change color of money cost bar to a darker one to avoid accessibility issues +* Bug #1403: Only regenerate codes if isCodeAutogenerated() is true +* Add a map in MoneyCostCalculator to cache calculated values +* Disable Money Cost Bar in company view to avoid performance issues +* Bug #1289: Added subcontractor name to tasks when showing resources is enabled +* Remove unneeded throws in MoneyCostCalculatorTest +* Add unit tests to check MoneyCostCalculator with a different type of hours +* Remove commented lines in MoneyCostCalculatorTest +* Update Copyright info in user documentation +* Improve sentence in "Imputed hours" tab editing a task +* Add information about budget in "Imputed hours" tab +* Add unit tests to check MoneyCostCalculator with a tree of tasks +* Remove unused parameters in CutyPrint.createCSSFile +* Add option to print money cost bar +* Add a new test case to check MoneyCostCalculator when there is not relationship via cost category +* Prevent possible rounding problems dividing BigDecimals +* Prevent NPE if there is not relationship between resource and type of hours via cost category +* Reload budget field in "General data" of templates +* Add field in "General data" tab to show the project budget +* Print Money Cost Bar proportinal to task size +* Prevent NPE calculating money cost for a TaskElement +* Remove method getMoneyCostBarPercentage from ITaskFundamentalProperties +* Improve tooltip message using budget, consumed money and percentage +* Using the new MoneyCostCalculator to print the new Money Cost bar +* Implement money cost calculation in a new class called MoneyCostCalculator +* Fix Money Cost Bar position in containers +* Add money cost percentage in the tooltip +* Change CSS for the money cost bar and reported hours bar +* Change icon for the new money cost bar +* Add new money cost bar at this moment using value, icon and color of reported hours +* doc: Update Fedora and openSUSE documentation for upgrade LibrePlan +* Merge branch 'libreplan-1.2' into money-cost-monitoring-system +* Fix typo in "Interporlation" (extra r) +* doc: Fix date format in on version at NEWS file +* Add no negative constraint in budget fields in edition forms +* Add budget field in order element template edition form +* Use budget field when creating a template from a task or vice versa +* Add budget field in order element details form +* Add budget cell in WBS +* Add new field budget to OrderLineTemplate +* Add basic tests for new attribute budget +* Add new field budget to OrderLine + + Version 1.2.2 (15 Mar 2012) --------------------------- diff --git a/VERSION b/VERSION index 23aa83906..0495c4a88 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.2 +1.2.3 diff --git a/debian/changelog b/debian/changelog index 57166d5b4..6174d72fc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libreplan (1.2.3-1) squeeze; urgency=low + + * Released LibrePlan 1.2.3 + + -- Jacobo Aragunde Pérez Wed, 18 Apr 2012 17:49:00 +0100 + libreplan (1.2.2-1) squeeze; urgency=low * Released LibrePlan 1.2.2 diff --git a/debian/libreplan.install b/debian/libreplan.install index 759c6e7ab..0926d1973 100644 --- a/debian/libreplan.install +++ b/debian/libreplan.install @@ -6,3 +6,4 @@ debian/tmp/usr/share/dbconfig-common/data/libreplan/upgrade/pgsql/1.1.0 debian/tmp/usr/share/dbconfig-common/data/libreplan/upgrade/pgsql/1.2.0 debian/tmp/usr/share/dbconfig-common/data/libreplan/upgrade/pgsql/1.2.1 debian/tmp/usr/share/dbconfig-common/data/libreplan/upgrade/pgsql/1.2.2 +debian/tmp/usr/share/dbconfig-common/data/libreplan/upgrade/pgsql/1.2.3 diff --git a/debian/rules b/debian/rules index fe8000eac..78640db69 100755 --- a/debian/rules +++ b/debian/rules @@ -90,6 +90,9 @@ install: # Copy SQL upgrade script for version 1.2.2 $(call CMD,cp $(CURDIR)/scripts/database/upgrade_1.2.2.sql \ $(CURDIR)/debian/tmp/usr/share/dbconfig-common/data/libreplan/upgrade/pgsql/1.2.2) + # Copy SQL upgrade script for version 1.2.3 + $(call CMD,cp $(CURDIR)/scripts/database/upgrade_1.2.3.sql \ + $(CURDIR)/debian/tmp/usr/share/dbconfig-common/data/libreplan/upgrade/pgsql/1.2.3) # Install Policy file $(call CMD,mkdir -p $(CURDIR)/debian/tmp/etc/tomcat6/policy.d) $(call CMD,cp $(CURDIR)/debian/51libreplan.policy \ diff --git a/doc/src/technical/howto-start-development-with-eclipse.rst b/doc/src/technical/howto-start-development-with-eclipse.rst index 7d676d590..b31919808 100644 --- a/doc/src/technical/howto-start-development-with-eclipse.rst +++ b/doc/src/technical/howto-start-development-with-eclipse.rst @@ -29,7 +29,7 @@ a) Clone Git repository (recommended):: b) Download last version source code:: - $ wget http://downloads.sourceforge.net/project/libreplan/files/LibrePlan/libreplan_1.2.0.tar.gz + $ wget http://downloads.sourceforge.net/project/libreplan/LibrePlan/libreplan_1.2.0.tar.gz $ tar -xzvf libreplan_1.2.0.tar.gz You should review ``HACKING`` file to check that you have installed all the diff --git a/doc/src/user/en/20-acerca-de.rst b/doc/src/user/en/20-acerca-de.rst index 699cce057..3cc32ab77 100644 --- a/doc/src/user/en/20-acerca-de.rst +++ b/doc/src/user/en/20-acerca-de.rst @@ -77,6 +77,7 @@ Translators Diego Pino García * [it] Giuseppe Zizza * [nl] Jeroen Baten +* [pl] Krzysztof Kamecki * [pt] Helena Grosso , Joaquim Rocha * [ru] Pavel Rudensky diff --git a/doc/src/user/es/20-acerca-de.rst b/doc/src/user/es/20-acerca-de.rst index 6c58945ea..dfceb6c21 100644 --- a/doc/src/user/es/20-acerca-de.rst +++ b/doc/src/user/es/20-acerca-de.rst @@ -77,6 +77,7 @@ Traductores Diego Pino García * [it] Giuseppe Zizza * [nl] Jeroen Baten +* [pl] Krzysztof Kamecki * [pt] Helena Grosso , Joaquim Rocha * [ru] Pavel Rudensky diff --git a/doc/src/user/gl/20-acerca-de.rst b/doc/src/user/gl/20-acerca-de.rst index 41951a38c..a4b489556 100644 --- a/doc/src/user/gl/20-acerca-de.rst +++ b/doc/src/user/gl/20-acerca-de.rst @@ -77,6 +77,7 @@ Traductores Diego Pino García * [it] Giuseppe Zizza * [nl] Jeroen Baten +* [pl] Krzysztof Kamecki * [pt] Helena Grosso , Joaquim Rocha * [ru] Pavel Rudensky diff --git a/ganttzk/pom.xml b/ganttzk/pom.xml index 36f4e0184..f39353834 100644 --- a/ganttzk/pom.xml +++ b/ganttzk/pom.xml @@ -6,7 +6,7 @@ org.libreplan libreplan - 1.2.2 + 1.2.3 ganttzk jar diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java b/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java index aafe0c38d..b0dec0e58 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java @@ -388,18 +388,19 @@ public class LeftTasksTreeRow extends GenericForwardComposer { } }); } catch (ParseException e) { - getStartDateTextBox().setValue( - dateFormat.format(task.getBeginDate() - .toDayRoundedDate())); + // Do nothing as textbox is rested in the next sentence } + getStartDateTextBox().setValue( + dateFormat.format(task.getBeginDate().toDayRoundedDate())); } else if (updatedComponent == getEndDateTextBox()) { try { Date newEnd = dateFormat.parse(getEndDateTextBox().getValue()); task.resizeTo(LocalDate.fromDateFields(newEnd)); } catch (ParseException e) { - getEndDateTextBox().setValue( - asString(task.getEndDate().toDayRoundedDate())); + // Do nothing as textbox is rested in the next sentence } + getEndDateTextBox().setValue( + asString(task.getEndDate().toDayRoundedDate())); } planner.updateTooltips(); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java index 57e7b17a1..3e77f624c 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java @@ -41,6 +41,7 @@ import org.zkoss.ganttz.data.GanttDiagramGraph.IDependenciesEnforcerHookFactory; import org.zkoss.ganttz.data.GanttDiagramGraph.INotificationAfterDependenciesEnforcement; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.data.constraint.Constraint.IConstraintViolationListener; +import org.zkoss.ganttz.extensions.IContextWithPlannerTask; import org.zkoss.ganttz.util.ConstraintViolationNotificator; import org.zkoss.ganttz.util.WeakReferencedListeners.Mode; @@ -471,6 +472,16 @@ public abstract class Task implements ITaskFundamentalProperties { } } + public static void reloadResourcesText(IContextWithPlannerTask context) { + Task task = context.getTask(); + task.reloadResourcesText(); + List parents = context.getMapper().getParents( + task); + for (TaskContainer each : parents) { + each.reloadResourcesText(); + } + } + public boolean isSubcontracted() { return fundamentalProperties.isSubcontracted(); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java index 70e87e7e7..5df22d59b 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java @@ -86,8 +86,8 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private Listbox listZoomLevels; - private final String FILTER_RESOURCES = _("by resources"); - private final String FILTER_CRITERIA = _("by criteria"); + private final String FILTER_RESOURCES = _("Resources"); + private final String FILTER_CRITERIA = _("Generic allocation criteria"); private String feedBackMessage; private Boolean filterbyResources; diff --git a/ganttzk/src/main/resources/i18n/es.po b/ganttzk/src/main/resources/i18n/es.po index 3573626b7..038e570f1 100644 --- a/ganttzk/src/main/resources/i18n/es.po +++ b/ganttzk/src/main/resources/i18n/es.po @@ -10,10 +10,10 @@ # Manuel Rego Casasnovas , 2010, 2011, 2012. msgid "" msgstr "" -"Project-Id-Version: 1.2.1\n" +"Project-Id-Version: libreplan-1.2.3\n" "Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" -"POT-Creation-Date: 2012-01-13 16:49+0100\n" -"PO-Revision-Date: 2012-01-13 16:00+0000\n" +"POT-Creation-Date: 2012-04-11 08:32+0200\n" +"PO-Revision-Date: 2012-04-11 08:35+0000\n" "Last-Translator: Manuel Rego Casasnovas \n" "Language-Team: Español\n" "MIME-Version: 1.0\n" @@ -22,12 +22,12 @@ msgstr "" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:215 -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:234 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:216 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:235 msgid "Erase" msgstr "Borrar" -#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:300 +#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:299 msgid "Add Dependency" msgstr "Añadir dependencia" @@ -35,11 +35,15 @@ msgstr "Añadir dependencia" msgid "Worker" msgstr "Trabajador" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:682 +msgid "Show money cost bar" +msgstr "Mostrar barra de coste monetario" + #: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 msgid "Start" msgstr "Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:635 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:661 msgid "Show reported hours" msgstr "Mostrar horas reportadas" @@ -64,7 +68,7 @@ msgstr "Recursos limitantes" msgid "The specified dependency is not allowed" msgstr "La dependencia especificada no está permitida" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:254 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:255 msgid "Set End-End" msgstr "Definir Fin-Fin" @@ -88,6 +92,10 @@ msgstr "esfuerzo disponible: {0}, esfuerzo asignado: {1}" msgid "Criterion" msgstr "Criterio" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:688 +msgid "Hide money cost bar" +msgstr "Ocultar barra de coste monetario" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:44 msgid "Print" msgstr "Imprimir" @@ -116,6 +124,10 @@ msgstr "Fin" msgid "changing zoom" msgstr "cambiando zoom" +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:91 +msgid "Show/Hide money cost bar" +msgstr "Mostrar/Ocultar barra de coste monetario" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 msgid "Quarter" msgstr "Trimestre" @@ -141,7 +153,7 @@ msgstr "Nombre" msgid "Name filter" msgstr "Filtro por nombres" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:615 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:641 msgid "Show progress" msgstr "Mostrar progreso" @@ -153,14 +165,14 @@ msgstr "Expandir/Plegar todo" msgid "Show/Hide critical path" msgstr "Mostrar/Ocultar camino crítico" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 -msgid "Year" -msgstr "Año" - #: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:150 msgid "showing criteria" msgstr "mostrando criterios" +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 +msgid "Year" +msgstr "Año" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:58 msgid "Month" msgstr "Mes" @@ -169,15 +181,15 @@ msgstr "Mes" msgid "Show/Hide resources" msgstr "Mostrar/Ocultar recursos" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:246 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:247 msgid "Set End-Start" msgstr "Definir Fin-Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:297 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:308 msgid "decreasing zoom" msgstr "reduciendo zoom" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:596 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:622 msgid "Hide critical path" msgstr "Ocultar camino crítico" @@ -185,7 +197,7 @@ msgstr "Ocultar camino crítico" msgid "Day" msgstr "Día" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:642 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:668 msgid "Hide reported hours" msgstr "Ocultar horas reportadas" @@ -206,11 +218,11 @@ msgid "Hour" msgstr "Hora" #: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:114 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:120 msgid "Graphics" msgstr "Gráficas" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:250 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:251 msgid "Set Start-Start" msgstr "Definir Inicio-Inicio" @@ -218,7 +230,7 @@ msgstr "Definir Inicio-Inicio" msgid "See resource allocation" msgstr "Ver asignación de recursos" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:280 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:291 msgid "increasing zoom" msgstr "aumentando zoom" @@ -234,10 +246,10 @@ msgstr "Todos" msgid "Refresh" msgstr "Actualizar" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:591 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:617 msgid "Show critical path" msgstr "Mostrar camino crítico" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:620 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:646 msgid "Hide progress" msgstr "Ocultar progreso" diff --git a/ganttzk/src/main/resources/i18n/fr.po b/ganttzk/src/main/resources/i18n/fr.po index acc323409..724947d76 100644 --- a/ganttzk/src/main/resources/i18n/fr.po +++ b/ganttzk/src/main/resources/i18n/fr.po @@ -8,10 +8,10 @@ # Philippe Poumaroux , 2012. msgid "" msgstr "" -"Project-Id-Version: 1.2.1\n" +"Project-Id-Version: libreplan-1.2.3\n" "Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" -"POT-Creation-Date: 2012-01-13 16:49+0100\n" -"PO-Revision-Date: 2012-01-23 12:36+0000\n" +"POT-Creation-Date: 2012-04-11 08:32+0200\n" +"PO-Revision-Date: 2012-04-11 12:30+0000\n" "Last-Translator: Philippe Poumaroux \n" "Language-Team: Français\n" "MIME-Version: 1.0\n" @@ -20,12 +20,12 @@ msgstr "" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:215 -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:234 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:216 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:235 msgid "Erase" msgstr "Supprimer" -#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:300 +#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:299 msgid "Add Dependency" msgstr "Ajouter une dépendance" @@ -33,11 +33,15 @@ msgstr "Ajouter une dépendance" msgid "Worker" msgstr "Travailleur" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:682 +msgid "Show money cost bar" +msgstr "Afficher la barre de coût monétaire" + #: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 msgid "Start" msgstr "Début" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:635 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:661 msgid "Show reported hours" msgstr "Afficher les heures comptabilisées" @@ -62,7 +66,7 @@ msgstr "Ressources bloquantes" msgid "The specified dependency is not allowed" msgstr "La dépendance indiquée n'est pas disponible" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:254 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:255 msgid "Set End-End" msgstr "Faire correspondre les achèvements" @@ -86,6 +90,10 @@ msgstr "investissement disponible: {0}, investissement affecté: {1}" msgid "Criterion" msgstr "Critère" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:688 +msgid "Hide money cost bar" +msgstr "Masquer la barre de coût monétaire" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:44 msgid "Print" msgstr "Imprimer" @@ -114,6 +122,10 @@ msgstr "Fin" msgid "changing zoom" msgstr "Changer le zoom" +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:91 +msgid "Show/Hide money cost bar" +msgstr "Afficher/Masquer la barre de coût monétaire" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 msgid "Quarter" msgstr "Trimestre" @@ -139,7 +151,7 @@ msgstr "Nom" msgid "Name filter" msgstr "Filtre par nom" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:615 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:641 msgid "Show progress" msgstr "Monter l'avancement" @@ -151,14 +163,14 @@ msgstr "Déplier/Replier tout" msgid "Show/Hide critical path" msgstr "Montrer/Cacher le chemin critique" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 -msgid "Year" -msgstr "Année" - #: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:150 msgid "showing criteria" msgstr "Montrer les critères" +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 +msgid "Year" +msgstr "Année" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:58 msgid "Month" msgstr "Mois" @@ -167,15 +179,15 @@ msgstr "Mois" msgid "Show/Hide resources" msgstr "Montrer/Cacher les ressources" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:246 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:247 msgid "Set End-Start" msgstr "Ajuster le début sur la fin" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:297 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:308 msgid "decreasing zoom" msgstr "réduire le zoom" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:596 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:622 msgid "Hide critical path" msgstr "Cacher le chemin critique" @@ -183,7 +195,7 @@ msgstr "Cacher le chemin critique" msgid "Day" msgstr "Jour" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:642 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:668 msgid "Hide reported hours" msgstr "Cacher les heures comptabilisées" @@ -204,11 +216,11 @@ msgid "Hour" msgstr "Heure" #: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:114 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:120 msgid "Graphics" msgstr "Graphiques" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:250 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:251 msgid "Set Start-Start" msgstr "Ajuster sur le début" @@ -216,7 +228,7 @@ msgstr "Ajuster sur le début" msgid "See resource allocation" msgstr "Voir l'allocation des ressources" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:280 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:291 msgid "increasing zoom" msgstr "augmenter le zoom" @@ -232,10 +244,10 @@ msgstr "Tout" msgid "Refresh" msgstr "Rafraîchir" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:591 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:617 msgid "Show critical path" msgstr "Montrer le chemin critique" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:620 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:646 msgid "Hide progress" msgstr "Cacher l'avancement" diff --git a/ganttzk/src/main/resources/i18n/gl.po b/ganttzk/src/main/resources/i18n/gl.po index 1f90df997..57515165c 100644 --- a/ganttzk/src/main/resources/i18n/gl.po +++ b/ganttzk/src/main/resources/i18n/gl.po @@ -10,11 +10,11 @@ # Manuel Rego Casasnovas , 2010, 2011, 2012. msgid "" msgstr "" -"Project-Id-Version: 1.2.1\n" +"Project-Id-Version: libreplan-1.2.3\n" "Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" -"POT-Creation-Date: 2012-01-13 16:49+0100\n" -"PO-Revision-Date: 2012-01-13 16:01+0000\n" -"Last-Translator: Manuel Rego Casasnovas\n" +"POT-Creation-Date: 2012-04-11 08:32+0200\n" +"PO-Revision-Date: 2012-04-11 08:34+0000\n" +"Last-Translator: Manuel Rego Casasnovas \n" "Language-Team: Galego\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -22,12 +22,12 @@ msgstr "" "Language: gl\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:215 -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:234 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:216 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:235 msgid "Erase" msgstr "Borrar" -#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:300 +#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:299 msgid "Add Dependency" msgstr "Engadir dependencia" @@ -35,11 +35,15 @@ msgstr "Engadir dependencia" msgid "Worker" msgstr "Traballador" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:682 +msgid "Show money cost bar" +msgstr "Mostrar barra de coste monetario" + #: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 msgid "Start" msgstr "Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:635 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:661 msgid "Show reported hours" msgstr "Mostrar horas reportadas" @@ -64,7 +68,7 @@ msgstr "Recursos limitantes" msgid "The specified dependency is not allowed" msgstr "A dependencia especificada non está permitida" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:254 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:255 msgid "Set End-End" msgstr "Definir Fin-Fin" @@ -88,6 +92,10 @@ msgstr "esforzo dispoñible: {0}, esforzo asignado: {1}" msgid "Criterion" msgstr "Criterio" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:688 +msgid "Hide money cost bar" +msgstr "Ocultar barra de coste monetario" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:44 msgid "Print" msgstr "Imprimir" @@ -116,6 +124,10 @@ msgstr "Fin" msgid "changing zoom" msgstr "cambiando zoom" +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:91 +msgid "Show/Hide money cost bar" +msgstr "Mostrar/Ocultar barra de coste monetario" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 msgid "Quarter" msgstr "Trimestre" @@ -141,7 +153,7 @@ msgstr "Nome" msgid "Name filter" msgstr "Filtro por nomes" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:615 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:641 msgid "Show progress" msgstr "Mostrar progreso" @@ -153,14 +165,14 @@ msgstr "Expandir/Pregar todo" msgid "Show/Hide critical path" msgstr "Mostrar/Ocultar camiño crítico" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 -msgid "Year" -msgstr "Ano" - #: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:150 msgid "showing criteria" msgstr "mostrando criterios" +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 +msgid "Year" +msgstr "Ano" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:58 msgid "Month" msgstr "Mes" @@ -169,15 +181,15 @@ msgstr "Mes" msgid "Show/Hide resources" msgstr "Mostrar/Ocultar recursos" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:246 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:247 msgid "Set End-Start" msgstr "Definir Fin-Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:297 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:308 msgid "decreasing zoom" msgstr "reducindo zoom" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:596 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:622 msgid "Hide critical path" msgstr "Ocultar camiño crítico" @@ -185,7 +197,7 @@ msgstr "Ocultar camiño crítico" msgid "Day" msgstr "Día" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:642 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:668 msgid "Hide reported hours" msgstr "Ocultar horas reportadas" @@ -206,11 +218,11 @@ msgid "Hour" msgstr "Hora" #: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:114 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:120 msgid "Graphics" msgstr "Gráficas" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:250 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:251 msgid "Set Start-Start" msgstr "Definir Inicio-Inicio" @@ -218,7 +230,7 @@ msgstr "Definir Inicio-Inicio" msgid "See resource allocation" msgstr "Ver asignación de recursoss" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:280 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:291 msgid "increasing zoom" msgstr "aumentando zoom" @@ -234,10 +246,10 @@ msgstr "Todos" msgid "Refresh" msgstr "Actualizar" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:591 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:617 msgid "Show critical path" msgstr "Mostrar camiño crítico" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:620 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:646 msgid "Hide progress" msgstr "Ocultar progreso" diff --git a/ganttzk/src/main/resources/i18n/it.po b/ganttzk/src/main/resources/i18n/it.po index ef0c84cde..87c269918 100644 --- a/ganttzk/src/main/resources/i18n/it.po +++ b/ganttzk/src/main/resources/i18n/it.po @@ -8,10 +8,10 @@ # Giuseppe Zizza , 2012. msgid "" msgstr "" -"Project-Id-Version: 1.2.1\n" +"Project-Id-Version: libreplan-1.2.3\n" "Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" -"POT-Creation-Date: 2012-01-13 16:49+0100\n" -"PO-Revision-Date: 2012-01-13 16:09+0000\n" +"POT-Creation-Date: 2012-04-11 08:32+0200\n" +"PO-Revision-Date: 2012-04-11 08:05+0000\n" "Last-Translator: Giuseppe Zizza \n" "Language-Team: Italiano\n" "MIME-Version: 1.0\n" @@ -20,12 +20,12 @@ msgstr "" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:215 -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:234 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:216 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:235 msgid "Erase" msgstr "Cancella" -#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:300 +#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:299 msgid "Add Dependency" msgstr "Aggiungi dipendenza" @@ -33,11 +33,15 @@ msgstr "Aggiungi dipendenza" msgid "Worker" msgstr "Lavoratore" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:682 +msgid "Show money cost bar" +msgstr "Mostra la barra dei costi" + #: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 msgid "Start" msgstr "Inizio" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:635 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:661 msgid "Show reported hours" msgstr "Mostra le ore riportate" @@ -62,7 +66,7 @@ msgstr "Risorse limitanti" msgid "The specified dependency is not allowed" msgstr "La dipendenza specifica non è permessa" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:254 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:255 msgid "Set End-End" msgstr "Imposta Fine-Fine" @@ -86,6 +90,10 @@ msgstr "Forza disponbile: {0}, forza assegnata: {1}" msgid "Criterion" msgstr "Criterio" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:688 +msgid "Hide money cost bar" +msgstr "Nascondi la barra dei costi" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:44 msgid "Print" msgstr "Stampa" @@ -114,6 +122,10 @@ msgstr "Fine" msgid "changing zoom" msgstr "Cambio vista" +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:91 +msgid "Show/Hide money cost bar" +msgstr "Mostra/Nascondi la barra dei costi" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 msgid "Quarter" msgstr "Quarto" @@ -139,7 +151,7 @@ msgstr "Nome" msgid "Name filter" msgstr "Nome filtro" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:615 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:641 msgid "Show progress" msgstr "Mostra progresso" @@ -151,14 +163,14 @@ msgstr "Espandi/Riduci tutti" msgid "Show/Hide critical path" msgstr "Mostra/Nascondi percorso critico" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 -msgid "Year" -msgstr "Anno" - #: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:150 msgid "showing criteria" msgstr "Mostro i criteri" +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 +msgid "Year" +msgstr "Anno" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:58 msgid "Month" msgstr "Mese" @@ -167,15 +179,15 @@ msgstr "Mese" msgid "Show/Hide resources" msgstr "Mostra/Nascondi risorse" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:246 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:247 msgid "Set End-Start" msgstr "Imposta Fine-Inizio" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:297 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:308 msgid "decreasing zoom" msgstr "Riduco ingrandimento" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:596 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:622 msgid "Hide critical path" msgstr "Nascondi percorso critico" @@ -183,7 +195,7 @@ msgstr "Nascondi percorso critico" msgid "Day" msgstr "Giorno" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:642 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:668 msgid "Hide reported hours" msgstr "Nascondi ore riportate" @@ -204,11 +216,11 @@ msgid "Hour" msgstr "Ora" #: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:114 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:120 msgid "Graphics" msgstr "Grafici" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:250 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:251 msgid "Set Start-Start" msgstr "Imposta Inizio-Inizio" @@ -216,7 +228,7 @@ msgstr "Imposta Inizio-Inizio" msgid "See resource allocation" msgstr "Mostra allocazione risorse" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:280 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:291 msgid "increasing zoom" msgstr "Aumento l'ingrandimento" @@ -232,10 +244,10 @@ msgstr "Tutti" msgid "Refresh" msgstr "Aggiorna" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:591 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:617 msgid "Show critical path" msgstr "Mostra percorso critico" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:620 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:646 msgid "Hide progress" msgstr "Nascondi progresso" diff --git a/ganttzk/src/main/resources/i18n/keys.pot b/ganttzk/src/main/resources/i18n/keys.pot index f0bff0848..1a555cf52 100644 --- a/ganttzk/src/main/resources/i18n/keys.pot +++ b/ganttzk/src/main/resources/i18n/keys.pot @@ -7,9 +7,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: 1.2.2\n" +"Project-Id-Version: libreplan-1.2.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-02-24 09:08+0100\n" +"POT-Creation-Date: 2012-04-11 08:32+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -31,11 +31,15 @@ msgstr "" msgid "Worker" msgstr "" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:682 +msgid "Show money cost bar" +msgstr "" + #: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 msgid "Start" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:635 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:661 msgid "Show reported hours" msgstr "" @@ -84,6 +88,10 @@ msgstr "" msgid "Criterion" msgstr "" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:688 +msgid "Hide money cost bar" +msgstr "" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:44 msgid "Print" msgstr "" @@ -112,6 +120,10 @@ msgstr "" msgid "changing zoom" msgstr "" +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:91 +msgid "Show/Hide money cost bar" +msgstr "" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 msgid "Quarter" msgstr "" @@ -137,7 +149,7 @@ msgstr "" msgid "Name filter" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:615 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:641 msgid "Show progress" msgstr "" @@ -149,14 +161,14 @@ msgstr "" msgid "Show/Hide critical path" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 -msgid "Year" -msgstr "" - #: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:150 msgid "showing criteria" msgstr "" +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 +msgid "Year" +msgstr "" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:58 msgid "Month" msgstr "" @@ -169,11 +181,11 @@ msgstr "" msgid "Set End-Start" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:297 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:308 msgid "decreasing zoom" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:596 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:622 msgid "Hide critical path" msgstr "" @@ -181,7 +193,7 @@ msgstr "" msgid "Day" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:642 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:668 msgid "Hide reported hours" msgstr "" @@ -202,7 +214,7 @@ msgid "Hour" msgstr "" #: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:114 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:120 msgid "Graphics" msgstr "" @@ -214,7 +226,7 @@ msgstr "" msgid "See resource allocation" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:280 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:291 msgid "increasing zoom" msgstr "" @@ -230,10 +242,10 @@ msgstr "" msgid "Refresh" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:591 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:617 msgid "Show critical path" msgstr "" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:620 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:646 msgid "Hide progress" msgstr "" diff --git a/ganttzk/src/main/resources/i18n/nl.po b/ganttzk/src/main/resources/i18n/nl.po index 339ca61b0..fde0eff2e 100644 --- a/ganttzk/src/main/resources/i18n/nl.po +++ b/ganttzk/src/main/resources/i18n/nl.po @@ -8,10 +8,10 @@ # Jeroen Baten , 2012. msgid "" msgstr "" -"Project-Id-Version: LibrePlan\n" +"Project-Id-Version: libreplan-1.2.3\n" "Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" -"POT-Creation-Date: 2012-02-24 09:08+0100\n" -"PO-Revision-Date: 2012-02-29 19:53+0000\n" +"POT-Creation-Date: 2012-04-11 08:32+0200\n" +"PO-Revision-Date: 2012-04-13 14:30+0000\n" "Last-Translator: Jeroen Baten \n" "Language-Team: Nederlands\n" "MIME-Version: 1.0\n" @@ -33,11 +33,15 @@ msgstr "Voeg afhankelijkheid toe" msgid "Worker" msgstr "Werker" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:682 +msgid "Show money cost bar" +msgstr "Toon geldelijke kosten balk" + #: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 msgid "Start" msgstr "Start" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:635 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:661 msgid "Show reported hours" msgstr "Toon gerapporteerde uren" @@ -86,6 +90,10 @@ msgstr "Beschikbare inspanning: {0}, toegewezen inspanning: {1}" msgid "Criterion" msgstr "Criterium" +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:688 +msgid "Hide money cost bar" +msgstr "Verberg geldelijke kosten balk" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:44 msgid "Print" msgstr "Afdrukken" @@ -114,6 +122,10 @@ msgstr "Einde" msgid "changing zoom" msgstr "wijzig zoom" +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:91 +msgid "Show/Hide money cost bar" +msgstr "Toon/verberg geldelijke kosten balk" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 msgid "Quarter" msgstr "Kwartaal" @@ -139,7 +151,7 @@ msgstr "Naam" msgid "Name filter" msgstr "Naam filter " -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:615 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:641 msgid "Show progress" msgstr "Toon voortgang" @@ -151,14 +163,14 @@ msgstr "Inklappen/Uitklappen alles" msgid "Show/Hide critical path" msgstr "Toon/Verberg kritieke pad" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 -msgid "Year" -msgstr "Jaar" - #: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:150 msgid "showing criteria" msgstr "tonen criteria" +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 +msgid "Year" +msgstr "Jaar" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:58 msgid "Month" msgstr "Maand" @@ -171,11 +183,11 @@ msgstr "Toon/Verberg resources" msgid "Set End-Start" msgstr "Set End-Start" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:297 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:308 msgid "decreasing zoom" msgstr "uitzoomen" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:596 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:622 msgid "Hide critical path" msgstr "Verberg kritieke pad" @@ -183,7 +195,7 @@ msgstr "Verberg kritieke pad" msgid "Day" msgstr "Dag" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:642 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:668 msgid "Hide reported hours" msgstr "Verberg gerapporteerde uren" @@ -204,7 +216,7 @@ msgid "Hour" msgstr "Uur" #: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:114 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:120 msgid "Graphics" msgstr "Graphics" @@ -216,7 +228,7 @@ msgstr "Set Start-Start" msgid "See resource allocation" msgstr "Zie resource allocation" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:280 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:291 msgid "increasing zoom" msgstr "Inzoomen" @@ -232,10 +244,10 @@ msgstr "Alle" msgid "Refresh" msgstr "Ververs" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:591 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:617 msgid "Show critical path" msgstr "Toon kritieke pad" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:620 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:646 msgid "Hide progress" msgstr "Verberg voortgang" diff --git a/ganttzk/src/main/resources/i18n/pl.po b/ganttzk/src/main/resources/i18n/pl.po new file mode 100644 index 000000000..5e726d76f --- /dev/null +++ b/ganttzk/src/main/resources/i18n/pl.po @@ -0,0 +1,253 @@ +# LibrePlan - GanttZK module. +# Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e +# Desenvolvemento Tecnolóxico de Galicia +# Copyright (C) 2010-2012 Igalia, S.L. +# This file is distributed under the same license as the LibrePlan package. +# +# Translators: +# Krzysztof Kamecki , 2012. +msgid "" +msgstr "" +"Project-Id-Version: libreplan-1.2.3\n" +"Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" +"POT-Creation-Date: 2012-04-11 08:32+0200\n" +"PO-Revision-Date: 2012-04-11 06:39+0000\n" +"Last-Translator: Krzysztof Kamecki \n" +"Language-Team: Polski\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:216 +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:235 +msgid "Erase" +msgstr "Usuń" + +#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:299 +msgid "Add Dependency" +msgstr "Dodaj Zależność" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Worker" +msgstr "Pracownik" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:682 +msgid "Show money cost bar" +msgstr "" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 +msgid "Start" +msgstr "Start" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:661 +msgid "Show reported hours" +msgstr "Pokaż zgłaszane godziny" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:90 +msgid "by criteria" +msgstr "po kryteriach" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:85 +msgid "Show/Hide reported hours" +msgstr "Pokaż/Ukryj zgłaszane godziny" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:35 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:49 +msgid "Zoom" +msgstr "Powiększ" + +#: ganttzk/src/main/java/org/zkoss/ganttz/TabsRegistry.java:121 +msgid "Limiting resources" +msgstr "Ograniczanie zasobów" + +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:67 +msgid "The specified dependency is not allowed" +msgstr "Określony zasób nie jest dozwolony" + +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:255 +msgid "Set End-End" +msgstr "Ustaw End-End" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:78 +msgid "Show/Hide progress" +msgstr "Pokaż/Ukryj postęp" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Task" +msgstr "Zadanie" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:89 +msgid "by resources" +msgstr "po zasobach" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java:199 +msgid "available effort: {0}, assigned effort: {1}" +msgstr "dostępny wysiłek: {0}, przydzielony wysiłek: {1}" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:64 +msgid "Criterion" +msgstr "Kryterium" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:688 +msgid "Hide money cost bar" +msgstr "" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:44 +msgid "Print" +msgstr "Drukuj" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:71 +msgid "Week" +msgstr "Tydzień" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:501 +msgid "filtering by name" +msgstr "filtruj po nazwie" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java:111 +msgid "See scheduling" +msgstr "Zobacz planowanie" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:43 +msgid "Show" +msgstr "Pokaż" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:30 +msgid "End" +msgstr "Koniec" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java:243 +msgid "changing zoom" +msgstr "zmiana przybliżenia" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:91 +msgid "Show/Hide money cost bar" +msgstr "" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 +msgid "Quarter" +msgstr "Kwartał" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "None" +msgstr "Żaden" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:71 +msgid "Flatten/Unflatten tree" +msgstr "Płaskie/Rozwinięte drzewo" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Project" +msgstr "Projekt" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:67 +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:28 +msgid "Name" +msgstr "Nazwa" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:50 +msgid "Name filter" +msgstr "Filtr nazwy" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:641 +msgid "Show progress" +msgstr "Pokaż postęp" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:67 +msgid "Expand/Collapse all" +msgstr "Rozwiń/Zwiń wszystkie" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:58 +msgid "Show/Hide critical path" +msgstr "Pokaż/Ukryj ścieżkę krytyczną" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:150 +msgid "showing criteria" +msgstr "pokaż kryteria" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 +msgid "Year" +msgstr "Rok" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:58 +msgid "Month" +msgstr "Miesiąc" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:64 +msgid "Show/Hide resources" +msgstr "Pokaż/Ukryj zasoby" + +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:247 +msgid "Set End-Start" +msgstr "Ustaw End-Start" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:308 +msgid "decreasing zoom" +msgstr "zmniejszenie powiększenia" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:622 +msgid "Hide critical path" +msgstr "ukryj ścieżkę krytyczną" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:84 +msgid "Day" +msgstr "Dzień" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:668 +msgid "Hide reported hours" +msgstr "Ukryj zgłoszone godziny" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java:193 +msgid "Load: {0}%" +msgstr "Ładowanie: {0}%" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:147 +msgid "showing resources" +msgstr "pokazuję zasoby" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:61 +msgid "Show/Hide labels" +msgstr "Pokaż/Ukryj etykiety" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:97 +msgid "Hour" +msgstr "Godzina" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:120 +msgid "Graphics" +msgstr "Grafika" + +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:251 +msgid "Set Start-Start" +msgstr "Ustaw Start-Start" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java:152 +msgid "See resource allocation" +msgstr "Pokaż przydział zasobów" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:291 +msgid "increasing zoom" +msgstr "powiększ powiększenie" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:467 +msgid "Show all elements" +msgstr "Pokaż wszystkie elementy" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:466 +msgid "All" +msgstr "Wszystko" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:38 +msgid "Refresh" +msgstr "Odśwież" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:617 +msgid "Show critical path" +msgstr "Pokaż ścieżkę krytyczną" + +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:646 +msgid "Hide progress" +msgstr "Ukryj postęp" diff --git a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul index c8c5bc61c..436004528 100644 --- a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul +++ b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul @@ -40,17 +40,17 @@ resourcesLoadPanel = self; - ${ganttzk_i18n:_('Show')}: - ${ganttzk_i18n:_('Page')}: + + + ${ganttzk_i18n:_('Group by')}: + - - - diff --git a/libreplan-business/pom.xml b/libreplan-business/pom.xml index ed7e38662..6cf08d447 100644 --- a/libreplan-business/pom.xml +++ b/libreplan-business/pom.xml @@ -5,7 +5,7 @@ org.libreplan libreplan - 1.2.2 + 1.2.3 libreplan-business jar @@ -134,7 +134,7 @@ org.liquibase liquibase-maven-plugin - 2.0-rc7 + 2.0.4 process-resources @@ -162,7 +162,7 @@ org.liquibase liquibase-maven-plugin - 2.0-rc7 + 2.0.4 process-resources @@ -205,7 +205,7 @@ org.liquibase liquibase-maven-plugin - [2.0-rc7,) + [2.0.4,) update diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/Configuration.java b/libreplan-business/src/main/java/org/libreplan/business/common/Configuration.java index c7b66b0bb..49a2e7db2 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/common/Configuration.java +++ b/libreplan-business/src/main/java/org/libreplan/business/common/Configuration.java @@ -2,6 +2,7 @@ * This file is part of LibrePlan * * Copyright (C) 2010-2011 Wireless Galicia, S.L. + * Copyright (C) 2012 Igalia, S.L. * * 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 @@ -19,24 +20,29 @@ package org.libreplan.business.common; -import org.libreplan.business.common.daos.IConfigurationDAO; -import org.springframework.beans.factory.annotation.Autowired; +import org.apache.commons.lang.BooleanUtils; + /** - * It contains the compiling option to disable the warning changing default - * password and implements of singleton pattern. + * This is a singleton that contains the compilation options passed from Maven. + * + * Currently we have two options: + *
    + *
  • Enable/Disable the warning changing default password
  • + *
  • Enable/Disable default users (such as user, wsreader and wswriter)
  • + *
* * @author Susana Montes Pedreira + * @author Manuel Rego Casasnovas */ public class Configuration { private static final Configuration singleton = new Configuration(); - @Autowired - private IConfigurationDAO configurationDAO; - private Boolean defaultPasswordsControl; + private Boolean exampleUsersDisabled; + private Configuration() { } @@ -61,4 +67,19 @@ public class Configuration { return defaultPasswordsControl; } + public void setExampleUsersDisabled(Boolean exampleUsersDisabled) { + this.exampleUsersDisabled = exampleUsersDisabled; + } + + public Boolean getExampleUsersDisabled() { + return exampleUsersDisabled; + } + + /** + * Returns the value of example users disabled compilation option + */ + public static boolean isExampleUsersDisabled() { + return BooleanUtils.isNotFalse(singleton.getExampleUsersDisabled()); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java index 3414abe4c..74ca6f149 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java +++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -91,6 +91,12 @@ public class Configuration extends BaseEntity { private Boolean allowToGatherUsageStatsEnabled = false; + /** + * Currency code according to ISO-4217 (3 letters) + */ + private String currencyCode = "EUR"; + private String currencySymbol = "€"; + public void setDefaultCalendar(BaseCalendar defaultCalendar) { this.defaultCalendar = defaultCalendar; } @@ -363,4 +369,22 @@ public class Configuration extends BaseEntity { this.allowToGatherUsageStatsEnabled = allowToGatherUsageStatsEnabled; } + @NotNull(message = "currency code not specified") + public String getCurrencyCode() { + return currencyCode; + } + + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; + } + + @NotNull(message = "currency symbol not specified") + public String getCurrencySymbol() { + return currencySymbol; + } + + public void setCurrencySymbol(String currencySymbol) { + this.currencySymbol = currencySymbol; + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/InfoComponent.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/InfoComponent.java index fe23533ae..2fd1cf33c 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/InfoComponent.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/InfoComponent.java @@ -27,8 +27,6 @@ import org.hibernate.validator.NotEmpty; */ public class InfoComponent { - private String code; - private String name; private String description; @@ -36,15 +34,6 @@ public class InfoComponent { public InfoComponent() { } - public void setCode(String code) { - this.code = code; - } - - @NotEmpty(message = "code not specified") - public String getCode() { - return code; - } - public void setName(String name) { this.name = name; } @@ -64,7 +53,6 @@ public class InfoComponent { public InfoComponent copy() { InfoComponent result = new InfoComponent(); - result.setCode(getCode()); result.setName(getName()); result.setDescription(getDescription()); return result; diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/InfoComponentWithCode.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/InfoComponentWithCode.java new file mode 100644 index 000000000..a1d313821 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/InfoComponentWithCode.java @@ -0,0 +1,47 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ +package org.libreplan.business.orders.entities; + +import org.hibernate.validator.NotEmpty; + +/** + * @author Jacobo Aragunde Pérez + */ +public class InfoComponentWithCode extends InfoComponent { + + private String code; + + public void setCode(String code) { + this.code = code; + } + + @NotEmpty(message = "code not specified") + public String getCode() { + return code; + } + + public InfoComponentWithCode copy() { + InfoComponentWithCode result = new InfoComponentWithCode(); + result.setCode(getCode()); + result.setName(getName()); + result.setDescription(getDescription()); + return result; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java index d1104a663..e9e834dce 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java @@ -75,7 +75,7 @@ import org.libreplan.business.workreports.entities.WorkReportLine; public abstract class OrderElement extends IntegrationEntity implements ICriterionRequirable, ITreeNode { - protected InfoComponent infoComponent = new InfoComponent(); + protected InfoComponentWithCode infoComponent = new InfoComponentWithCode(); private Date initDate; @@ -297,6 +297,16 @@ public abstract class OrderElement extends IntegrationEntity implements //we have to remove the TaskSource which contains a TaskGroup instead of TaskElement removeTaskSource(result); } + if(currentTaskSourceIsNotTheSame()) { + //this element was unscheduled and then scheduled again. Its TaskSource has + //been recreated but we have to remove the old one. + if(!getParent().currentTaskSourceIsNotTheSame()) { + //we only remove the TaskSource if the parent is not in the same situation. + //In case the parent is in the same situation, it will remove the related + //TaskSources in children tasks. + removeTaskSource(result); + } + } result .addAll(synchronizationForSchedulingPoint(schedulingDataForVersion)); } else if (isSuperElementPartialOrCompletelyScheduled()) { @@ -304,6 +314,15 @@ public abstract class OrderElement extends IntegrationEntity implements if (wasASchedulingPoint()) { result.add(taskSourceRemoval()); } + if(currentTaskSourceIsNotTheSame()) { + //all the children of this element were unscheduled and then scheduled again, + //its TaskSource has been recreated but we have to remove the old one. + if(getParent() == null || !getParent().currentTaskSourceIsNotTheSame()) { + //if it's a container node inside another container we could have the + //same problem than in the case of leaf tasks. + result.add(taskSourceRemoval()); + } + } result .add(synchronizationForSuperelement(schedulingDataForVersion)); } else if (schedulingState.isNoScheduled()) { @@ -331,6 +350,10 @@ public abstract class OrderElement extends IntegrationEntity implements .getSchedulingStateType(); } + protected boolean currentTaskSourceIsNotTheSame() { + return getOnDBTaskSource() != getTaskSource(); + } + private List childrenSynchronizations() { List childrenOfGroup = new ArrayList(); for (OrderElement orderElement : getSomewhatScheduledOrderElements()) { @@ -1267,9 +1290,9 @@ public abstract class OrderElement extends IntegrationEntity implements } @Valid - public InfoComponent getInfoComponent() { + public InfoComponentWithCode getInfoComponent() { if (infoComponent == null) { - infoComponent = new InfoComponent(); + infoComponent = new InfoComponentWithCode(); } return infoComponent; } diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/CompanyEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/CompanyEarnedValueCalculator.java new file mode 100644 index 000000000..c9420ba1b --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/CompanyEarnedValueCalculator.java @@ -0,0 +1,159 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.calendars.entities.AvailabilityTimeLine; +import org.libreplan.business.calendars.entities.AvailabilityTimeLine.Interval; +import org.libreplan.business.hibernate.notification.PredefinedDatabaseSnapshots; +import org.libreplan.business.workreports.entities.WorkReportLine; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Diego Pino García + */ +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class CompanyEarnedValueCalculator extends EarnedValueCalculator implements ICompanyEarnedValueCalculator { + + @Autowired + private PredefinedDatabaseSnapshots databaseSnapshots; + + @Override + @Transactional(readOnly = true) + public SortedMap calculateBudgetedCostWorkScheduled(AvailabilityTimeLine.Interval interval) { + Map> estimatedCostPerTask = databaseSnapshots + .snapshotEstimatedCostPerTask(); + Collection list = filterTasksByDate( + estimatedCostPerTask.keySet(), interval); + SortedMap estimatedCost = new TreeMap(); + + for (TaskElement each : list) { + addCost(estimatedCost, estimatedCostPerTask.get(each)); + } + return accumulateResult(estimatedCost); + } + + private List filterTasksByDate( + Collection tasks, + AvailabilityTimeLine.Interval interval) { + List result = new ArrayList(); + for(TaskElement task : tasks) { + if (interval.includes(task.getStartAsLocalDate()) + || interval.includes(task.getEndAsLocalDate())) { + result.add(task); + } + } + return result; + } + + private List filterWorkReportLinesByDate( + Collection lines, + AvailabilityTimeLine.Interval interval) { + List result = new ArrayList(); + for(WorkReportLine line: lines) { + if (interval.includes(line.getLocalDate())) { + result.add(line); + } + } + return result; + } + + private void addCost(SortedMap currentCost, + SortedMap additionalCost) { + for (LocalDate day : additionalCost.keySet()) { + if (!currentCost.containsKey(day)) { + currentCost.put(day, BigDecimal.ZERO); + } + currentCost.put(day, currentCost.get(day).add( + additionalCost.get(day))); + } + } + + private SortedMap accumulateResult( + SortedMap map) { + SortedMap result = new TreeMap(); + if (map.isEmpty()) { + return result; + } + + BigDecimal accumulatedResult = BigDecimal.ZERO; + for (LocalDate day : map.keySet()) { + BigDecimal value = map.get(day); + accumulatedResult = accumulatedResult.add(value); + result.put(day, accumulatedResult); + } + + return result; + } + + @Override + public SortedMap calculateActualCostWorkPerformed( + Interval interval) { + SortedMap result = new TreeMap(); + Collection workReportLines = filterWorkReportLinesByDate( + databaseSnapshots.snapshotWorkReportLines(), + interval); + + if (workReportLines.isEmpty()) { + return result; + } + + for (WorkReportLine workReportLine : workReportLines) { + LocalDate day = new LocalDate(workReportLine.getDate()); + BigDecimal cost = workReportLine.getEffort() + .toHoursAsDecimalWithScale(2); + + if (!result.containsKey(day)) { + result.put(day, BigDecimal.ZERO); + } + result.put(day, result.get(day).add(cost)); + } + return accumulateResult(result); + } + + @Override + public SortedMap calculateBudgetedCostWorkPerformed( + Interval interval) { + Map> advanceCostPerTask = databaseSnapshots + .snapshotAdvanceCostPerTask(); + Collection tasks = filterTasksByDate( + advanceCostPerTask.keySet(), interval); + + SortedMap result = new TreeMap(); + for (TaskElement each : tasks) { + addCost(result, advanceCostPerTask.get(each)); + } + return result; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/EarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/EarnedValueCalculator.java new file mode 100644 index 000000000..98bd28741 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/EarnedValueCalculator.java @@ -0,0 +1,252 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.joda.time.LocalDate; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Calculates generic 'Earned Value' indicators (those calculated out of + * BCWP, ACWP and BCWS + */ +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class EarnedValueCalculator implements IEarnedValueCalculator { + + @Override + public SortedMap calculateCostVariance( + SortedMap bcwp, + SortedMap acwp) { + return substract(bcwp, acwp); + } + + @Override + public SortedMap calculateScheduleVariance( + SortedMap bcwp, + SortedMap bcws) { + return substract(bcwp, bcws); + } + + @Override + public SortedMap calculateBudgetAtCompletion( + SortedMap bcws) { + SortedMap result = new TreeMap(); + BigDecimal value = Collections.max(bcws.values()); + for (LocalDate day : bcws.keySet()) { + result.put(day, value); + } + return result; + } + + @Override + public SortedMap calculateEstimateAtCompletion( + SortedMap acwp, + SortedMap bcwp, + SortedMap bac) { + return multiply(divide(acwp, bcwp, + BigDecimal.ZERO), bac); + } + + @Override + public SortedMap calculateVarianceAtCompletion( + SortedMap bac, + SortedMap eac) { + return substract(bac, eac); + } + + @Override + public SortedMap calculateEstimatedToComplete( + SortedMap eac, + SortedMap acwp) { + return substract(eac, acwp); + } + + @Override + public SortedMap calculateCostPerformanceIndex( + SortedMap bcwp, + SortedMap acwp) { + return divide(bcwp, acwp, BigDecimal.ZERO); + } + + @Override + public SortedMap calculateSchedulePerformanceIndex( + SortedMap bcwp, + SortedMap bcws) { + return divide(bcwp, bcws, BigDecimal.ZERO); + } + + private SortedMap substract( + SortedMap minuend, + SortedMap subtrahend) { + final SortedMap result = new TreeMap(); + forValuesAtSameKey(minuend, subtrahend, substractionOperation(result)); + return result; + + } + + public static void forValuesAtSameKey(Map a, Map b, + IOperation onSameKey) { + for (Entry each : a.entrySet()) { + V aValue = each.getValue(); + V bValue = b.get(each.getKey()); + onSameKey.operate(each.getKey(), aValue, bValue); + } + } + + private static IOperation substractionOperation( + final SortedMap result) { + return notNullOperands(new IOperation() { + + @Override + public void operate(LocalDate key, BigDecimal minuedValue, + BigDecimal subtrahendValue) { + result.put(key, minuedValue.subtract(subtrahendValue)); + } + + @Override + public void undefinedFor(LocalDate key) { + } + }); + } + + public static IOperation notNullOperands( + final IOperation operation) { + return new PreconditionChecker(operation) { + @Override + protected boolean isOperationDefinedFor(K key, V a, V b) { + return a != null && b != null; + } + }; + } + + public interface IOperation { + + public void operate(K key, V a, V b); + + public void undefinedFor(K key); + } + + protected static abstract class PreconditionChecker implements + IOperation { + + private final IOperation decorated; + + protected PreconditionChecker(IOperation decorated) { + this.decorated = decorated; + } + + @Override + public void operate(K key, V a, V b) { + if (isOperationDefinedFor(key, a, b)) { + decorated.operate(key, a, b); + } else { + decorated.undefinedFor(key); + } + } + + protected abstract boolean isOperationDefinedFor(K key, V a, V b); + + @Override + public void undefinedFor(K key) { + decorated.undefinedFor(key); + } + + } + + private static SortedMap multiply( + Map firstFactor, + Map secondFactor) { + final SortedMap result = new TreeMap(); + forValuesAtSameKey(firstFactor, secondFactor, + multiplicationOperation(result)); + return result; + } + + private static IOperation multiplicationOperation( + final SortedMap result) { + return notNullOperands(new IOperation() { + + @Override + public void operate(LocalDate key, BigDecimal a, + BigDecimal b) { + result.put(key, a.multiply(b)); + } + + @Override + public void undefinedFor(LocalDate key) { + result.put(key, BigDecimal.ZERO); + } + }); + } + + private static SortedMap divide( + Map dividend, + Map divisor, + final BigDecimal defaultIfNotComputable) { + final TreeMap result = new TreeMap(); + forValuesAtSameKey(dividend, divisor, + divisionOperation(result, defaultIfNotComputable)); + return result; + } + + private static IOperation divisionOperation( + final TreeMap result, + final BigDecimal defaultIfNotComputable) { + return notNullOperands(secondOperandNotZero(new IOperation() { + + @Override + public void operate(LocalDate key, BigDecimal dividendValue, + BigDecimal divisorValue) { + result.put(key, + dividendValue.divide(divisorValue, RoundingMode.DOWN)); + } + + @Override + public void undefinedFor(LocalDate key) { + result.put(key, defaultIfNotComputable); + } + })); + } + + public static IOperation secondOperandNotZero( + final IOperation operation) { + return new PreconditionChecker(operation) { + @Override + protected boolean isOperationDefinedFor(K key, BigDecimal a, + BigDecimal b) { + return b.signum() != 0; + } + }; + } + +} \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ICompanyEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ICompanyEarnedValueCalculator.java new file mode 100644 index 000000000..4f9e0d781 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ICompanyEarnedValueCalculator.java @@ -0,0 +1,44 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.SortedMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.calendars.entities.AvailabilityTimeLine; + +/** + * @author Diego Pino García + * + * Utility class for calculating all 'Earned Value' indicators + */ +public interface ICompanyEarnedValueCalculator extends IEarnedValueCalculator { + + SortedMap calculateBudgetedCostWorkScheduled( + AvailabilityTimeLine.Interval interval); + + SortedMap calculateActualCostWorkPerformed( + AvailabilityTimeLine.Interval interval); + + SortedMap calculateBudgetedCostWorkPerformed( + AvailabilityTimeLine.Interval interval); + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IEarnedValueCalculator.java new file mode 100644 index 000000000..13776a1e1 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IEarnedValueCalculator.java @@ -0,0 +1,74 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.SortedMap; + +import org.joda.time.LocalDate; + +/** + * @author Diego Pino García + * + * Utility class for calculating all 'Earned Value' indicators + */ +public interface IEarnedValueCalculator { + + // CV = BCWP - ACWP + SortedMap calculateCostVariance( + SortedMap bcwp, + SortedMap acwp); + + // SV = BCWP - BCWS + SortedMap calculateScheduleVariance( + SortedMap bcwp, + SortedMap bcws); + + // BAC = max (BCWS) + SortedMap calculateBudgetAtCompletion( + SortedMap bcws); + + // EAC = (ACWP/BCWP) * BAC + SortedMap calculateEstimateAtCompletion( + SortedMap acwp, + SortedMap bcwp, + SortedMap bac); + + // VAC = BAC - EAC + SortedMap calculateVarianceAtCompletion( + SortedMap bac, + SortedMap eac); + + // ETC = EAC - ACWP + SortedMap calculateEstimatedToComplete( + SortedMap eac, + SortedMap acwp); + + // SPI = BCWP / BCWS + SortedMap calculateSchedulePerformanceIndex( + SortedMap bcwp, + SortedMap bcws); + + // CPI = BCWP / ACWP + SortedMap calculateCostPerformanceIndex( + SortedMap bcwp, + SortedMap acwp); + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IOrderEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IOrderEarnedValueCalculator.java new file mode 100644 index 000000000..232f3ee0d --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IOrderEarnedValueCalculator.java @@ -0,0 +1,66 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.SortedMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.orders.entities.Order; + +/** + * @author Diego Pino García + * + * Utility class for calculating all 'Earned Value' indicators + */ +public interface IOrderEarnedValueCalculator extends IEarnedValueCalculator { + + // ACWP (Actual Cost Work Performed) + SortedMap calculateActualCostWorkPerformed( + Order order); + + // BCWP (Budgeted Cost Work Performed) + SortedMap calculateBudgetedCostWorkPerformed( + Order order); + + // BCWS (Budgeted Cost Work Scheduled) + SortedMap calculateBudgetedCostWorkScheduled(Order order); + + // ACWP at date + BigDecimal getActualCostWorkPerformedAt(Order order, LocalDate date); + + // BAC (Budget at Completion) + BigDecimal getBudgetAtCompletion(Order order); + + // BCWP at date + BigDecimal getBudgetedCostWorkPerformedAt(Order order, LocalDate date); + + // CPI (Cost Performance Index) + BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, + BigDecimal actualCost); + + // CV (Cost Variance) + BigDecimal getCostVariance(BigDecimal budgetedCost, BigDecimal actualCost); + + // EAC (Estimate At Completion) + BigDecimal getEstimateAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal costPerformanceIndex); + +} \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/OrderEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/OrderEarnedValueCalculator.java new file mode 100644 index 000000000..788229667 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/OrderEarnedValueCalculator.java @@ -0,0 +1,181 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.orders.entities.Order; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Diego Pino García + */ +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class OrderEarnedValueCalculator extends EarnedValueCalculator implements IOrderEarnedValueCalculator { + + @Autowired + private ICostCalculator hoursCostCalculator; + + @Transactional(readOnly = true) + @Override + public BigDecimal getActualCostWorkPerformedAt(Order order, LocalDate date) { + SortedMap actualCost = calculateActualCostWorkPerformed(order); + BigDecimal result = actualCost.get(date); + return (result != null) ? result : BigDecimal.ZERO; + } + + @Transactional(readOnly = true) + @Override + public SortedMap calculateActualCostWorkPerformed( + Order order) { + SortedMap result = new TreeMap(); + for (TaskElement taskElement : getAllTaskElements(order)) { + if (taskElement instanceof Task) { + addCost(result, getWorkReportCost((Task) taskElement)); + } + } + return accumulateResult(result); + } + + private SortedMap accumulateResult( + SortedMap map) { + SortedMap result = new TreeMap(); + if (map.isEmpty()) { + return result; + } + + BigDecimal accumulatedResult = BigDecimal.ZERO; + for (LocalDate day : map.keySet()) { + BigDecimal value = map.get(day); + accumulatedResult = accumulatedResult.add(value); + result.put(day, accumulatedResult); + } + return result; + } + + private void addCost(SortedMap currentCost, + SortedMap additionalCost) { + for (LocalDate day : additionalCost.keySet()) { + if (!currentCost.containsKey(day)) { + currentCost.put(day, BigDecimal.ZERO); + } + currentCost.put(day, + currentCost.get(day).add(additionalCost.get(day))); + } + } + + private List getAllTaskElements(Order order) { + List result = order.getAllChildrenAssociatedTaskElements(); + result.add(order.getAssociatedTaskElement()); + return result; + } + + private SortedMap getWorkReportCost(Task taskElement) { + return hoursCostCalculator.getWorkReportCost(taskElement); + } + + @Override + @Transactional(readOnly = true) + public BigDecimal getBudgetAtCompletion(Order order) { + SortedMap budgedtedCost = calculateBudgetedCostWorkScheduled(order); + LocalDate lastKey = budgedtedCost.lastKey(); + return (lastKey) != null ? budgedtedCost.get(lastKey) : BigDecimal.ZERO; + } + + @Override + @Transactional(readOnly = true) + public SortedMap calculateBudgetedCostWorkScheduled( + Order order) { + SortedMap result = new TreeMap(); + for (TaskElement taskElement : getAllTaskElements(order)) { + if (taskElement instanceof Task) { + addCost(result, getEstimatedCost((Task) taskElement)); + } + } + return accumulateResult(result); + } + + private SortedMap getEstimatedCost(Task task) { + return hoursCostCalculator.getEstimatedCost(task); + } + + @Override + @Transactional(readOnly = true) + public BigDecimal getBudgetedCostWorkPerformedAt(Order order, LocalDate date) { + SortedMap budgetedCost = calculateBudgetedCostWorkPerformed(order); + BigDecimal result = budgetedCost.get(date); + return (result != null) ? result : BigDecimal.ZERO; + } + + @Override + @Transactional(readOnly = true) + public SortedMap calculateBudgetedCostWorkPerformed( + Order order) { + SortedMap estimatedCost = new TreeMap(); + for (TaskElement taskElement : getAllTaskElements(order)) { + if (taskElement instanceof Task) { + addCost(estimatedCost, getAdvanceCost((Task) taskElement)); + } + } + return accumulateResult(estimatedCost); + } + + private SortedMap getAdvanceCost(Task task) { + return hoursCostCalculator.getAdvanceCost(task); + } + + @Override + public BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, + BigDecimal actualCost) { + if (BigDecimal.ZERO.compareTo(actualCost) == 0) { + return BigDecimal.ZERO; + } + return asPercentage(budgetedCost.divide(actualCost)); + } + + private BigDecimal asPercentage(BigDecimal value) { + return value.multiply(BigDecimal.valueOf(100)).setScale(2); + } + + @Override + public BigDecimal getCostVariance(BigDecimal budgetedCost, + BigDecimal actualCost) { + return budgetedCost.subtract(actualCost); + } + + @Override + public BigDecimal getEstimateAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal costPerformanceIndex) { + if (BigDecimal.ZERO.compareTo(costPerformanceIndex) == 0) { + return BigDecimal.ZERO; + } + return asPercentage(budgetAtCompletion.divide(costPerformanceIndex)); + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ResourceAllocation.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ResourceAllocation.java index 5c2eab504..36026027e 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ResourceAllocation.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ResourceAllocation.java @@ -209,9 +209,40 @@ public abstract class ResourceAllocation extends public static AllocationsSpecified allocating( List resourceAllocations) { + sortResourceAllocations(resourceAllocations); return new AllocationsSpecified(resourceAllocations); } + /** + * Specific allocations should be done first in order to generic allocations + * selects the less charged resources if there are several allocations in + * the same task + * + * @param resourceAllocations + * Sorted with specific allocations before generic ones + */ + private static void sortResourceAllocations( + List resourceAllocations) { + Collections.sort(resourceAllocations, + new Comparator() { + + @Override + public int compare(ResourcesPerDayModification o1, + ResourcesPerDayModification o2) { + if (o1.isSpecific() && o2.isSpecific()) { + return 0; + } + if (o1.isSpecific()) { + return -1; + } + if (o2.isSpecific()) { + return 1; + } + return 0; + } + }); + } + private static void checkStartLessOrEqualToEnd(IntraDayDate startInclusive, IntraDayDate endExclusive) { Validate.isTrue(startInclusive.compareTo(endExclusive) <= 0, @@ -2123,7 +2154,7 @@ public abstract class ResourceAllocation extends getDayAssignmentsState().detachAssignments(); } - void associateAssignmentsToResource() { + public void associateAssignmentsToResource() { for (DayAssignment dayAssignment : getAssignments()) { dayAssignment.associateToResource(); } diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java index a490b6bf3..4947700ab 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -73,6 +73,7 @@ import org.libreplan.business.workingday.ResourcesPerDay; /** * @author Óscar González Fernández + * @author Manuel Rego Casasnovas */ public class Task extends TaskElement implements ITaskPositionConstrained { @@ -1218,4 +1219,10 @@ public class Task extends TaskElement implements ITaskPositionConstrained { public void resetStatus() { this.currentStatus = null; } + + @Override + public boolean isAnyTaskWithConstraint(PositionConstraintType type) { + return getPositionConstraint().getConstraintType().equals(type); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java index 7ddc7d3ac..23262c025 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -63,6 +63,7 @@ import org.libreplan.business.workingday.ResourcesPerDay; /** * @author Óscar González Fernández + * @author Manuel Rego Casasnovas */ public abstract class TaskElement extends BaseEntity { @@ -769,4 +770,6 @@ public abstract class TaskElement extends BaseEntity { return null; } + public abstract boolean isAnyTaskWithConstraint(PositionConstraintType type); + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskGroup.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskGroup.java index c06d5896b..0edfd3817 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskGroup.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskGroup.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -44,6 +44,7 @@ import org.libreplan.business.workingday.IntraDayDate; /** * @author Óscar González Fernández * @author Javier Moran Rua + * @author Manuel Rego Casasnovas */ public class TaskGroup extends TaskElement { @@ -376,4 +377,15 @@ public class TaskGroup extends TaskElement { public void resetStatus() { this.isFinished = this.isInProgress = null; } + + @Override + public boolean isAnyTaskWithConstraint(PositionConstraintType type) { + for (TaskElement taskElement : getChildren()) { + if (taskElement.isAnyTaskWithConstraint(type)) { + return true; + } + } + return false; + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskMilestone.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskMilestone.java index e744eb35d..2583920f4 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskMilestone.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskMilestone.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -43,6 +43,7 @@ import org.libreplan.business.workingday.IntraDayDate; /** * @author Lorenzo Tilve Álvaro * @author Javier Moran Rua + * @author Manuel Rego Casasnovas */ public class TaskMilestone extends TaskElement implements ITaskPositionConstrained { @@ -225,4 +226,9 @@ public class TaskMilestone extends TaskElement implements ITaskPositionConstrain return false; } + @Override + public boolean isAnyTaskWithConstraint(PositionConstraintType type) { + return getPositionConstraint().getConstraintType().equals(type); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java index 8f547eaf5..a386bb968 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java @@ -127,6 +127,11 @@ public abstract class ResourcesPerDayModification extends return genericAllocation.createEffortDistributor(getResources()); } + @Override + public boolean isSpecific() { + return false; + } + } private static class OnSpecificAllocation extends @@ -194,6 +199,11 @@ public abstract class ResourcesPerDayModification extends return resourceAllocation.createEffortDistributor(); } + @Override + public boolean isSpecific() { + return true; + } + } public static ResourcesPerDayModification create( @@ -319,4 +329,6 @@ public abstract class ResourcesPerDayModification extends getBeingModified().getNonConsolidatedResourcePerDay()); } + public abstract boolean isSpecific(); + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/UntilFillingHoursAllocator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/UntilFillingHoursAllocator.java index f94c10808..e1010240a 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/UntilFillingHoursAllocator.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/allocationalgorithms/UntilFillingHoursAllocator.java @@ -29,7 +29,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; @@ -89,8 +88,19 @@ public abstract class UntilFillingHoursAllocator { IntraDayDate candidate = untilAllocating(dateFromWhichToAllocate, each.allocation, each.duration); currentResult = pickCurrentOrCandidate(currentResult, candidate); + setNewDataForAllocation(each.allocation, currentResult); + + // This is done in order that new assignments are taken into account + // for the next allocations in the same task + each.allocation.getResourceAllocation() + .associateAssignmentsToResource(); } - setAssignmentsForEachAllocation(currentResult); + // Then we detach the day assignments as they are going to be associated + // again later + for (EffortPerAllocation each : effortPerAllocation) { + each.allocation.getResourceAllocation().detach(); + } + return currentResult; } @@ -228,22 +238,16 @@ public abstract class UntilFillingHoursAllocator { } } - private void setAssignmentsForEachAllocation(IntraDayDate resultDate) { - for (Entry> entry : resultAssignments - .entrySet()) { - setNewDataForAllocation(entry, resultDate); - } - } - private void setNewDataForAllocation( - Entry> entry, + ResourcesPerDayModification resourcesPerDayModification, IntraDayDate resultDate) { @SuppressWarnings("unchecked") - ResourceAllocation allocation = (ResourceAllocation) entry - .getKey().getBeingModified(); - ResourcesPerDay resourcesPerDay = entry.getKey().getGoal(); + ResourceAllocation allocation = (ResourceAllocation) resourcesPerDayModification + .getBeingModified(); + ResourcesPerDay resourcesPerDay = resourcesPerDayModification.getGoal(); @SuppressWarnings("unchecked") - List value = (List) entry.getValue(); + List value = (List) resultAssignments + .get(resourcesPerDayModification); setNewDataForAllocation(allocation, resultDate, resourcesPerDay, value); } diff --git a/libreplan-business/src/main/java/org/libreplan/business/reports/dtos/HoursWorkedPerResourceDTO.java b/libreplan-business/src/main/java/org/libreplan/business/reports/dtos/HoursWorkedPerResourceDTO.java index 0de687547..a77bae0d4 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/reports/dtos/HoursWorkedPerResourceDTO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/reports/dtos/HoursWorkedPerResourceDTO.java @@ -38,9 +38,9 @@ public class HoursWorkedPerResourceDTO implements Comparable { private Date date; - private LocalTime clockStart; + private String clockStart; - private LocalTime clockFinish; + private String clockFinish; private EffortDuration effort; @@ -59,8 +59,12 @@ public class HoursWorkedPerResourceDTO implements Comparable { this.workerName = resource.getName(); this.date = workReportLine.getDate(); - this.clockStart = workReportLine.getClockStart(); - this.clockFinish = workReportLine.getClockFinish(); + LocalTime clockStart = workReportLine.getClockStart(); + this.clockStart = (clockStart != null) ? clockStart.toString("HH:mm") + : ""; + LocalTime clockFinish = workReportLine.getClockFinish(); + this.clockFinish = (clockFinish != null) ? clockFinish + .toString("HH:mm") : ""; this.effort = workReportLine.getEffort(); this.orderElementCode = workReportLine.getOrderElement().getCode(); this.orderElementName = workReportLine.getOrderElement().getName(); @@ -98,19 +102,19 @@ public class HoursWorkedPerResourceDTO implements Comparable { this.effort = effort; } - public LocalTime getClockStart() { + public String getClockStart() { return clockStart; } - public void setClockStart(LocalTime clockStart) { + public void setClockStart(String clockStart) { this.clockStart = clockStart; } - public LocalTime getClockFinish() { + public String getClockFinish() { return clockFinish; } - public void setClockFinish(LocalTime clockFinish) { + public void setClockFinish(String clockFinish) { this.clockFinish = clockFinish; } diff --git a/libreplan-business/src/main/java/org/libreplan/business/settings/entities/Language.java b/libreplan-business/src/main/java/org/libreplan/business/settings/entities/Language.java index 1b12bb406..926712078 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/settings/entities/Language.java +++ b/libreplan-business/src/main/java/org/libreplan/business/settings/entities/Language.java @@ -2,6 +2,7 @@ * This file is part of LibrePlan * * Copyright (C) 2011 ComtecSF, S.L. + * Copyright (C) 2012 Igalia, S.L. * * 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 @@ -28,6 +29,7 @@ import java.util.Locale; * * @author Cristina Alavarino Perez * @author Ignacio Diaz Teijido + * @author Manuel Rego Casasnovas */ public enum Language { @@ -39,7 +41,8 @@ public enum Language { PORTUGUESE_LANGUAGE("Português", new Locale("pt")), ITALIAN_LANGUAGE("Italiano", new Locale("it")), FRENCH_LANGUAGE("Français", new Locale("fr")), - DUTCH_LANGUAGE("Nederlands", new Locale("nl")); + DUTCH_LANGUAGE("Nederlands", new Locale("nl")), + POLISH_LANGUAGE("Polski", new Locale("pl")); private final String displayName; diff --git a/libreplan-business/src/main/java/org/libreplan/business/templates/entities/OrderElementTemplate.java b/libreplan-business/src/main/java/org/libreplan/business/templates/entities/OrderElementTemplate.java index c94d95535..cb1002bc6 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/templates/entities/OrderElementTemplate.java +++ b/libreplan-business/src/main/java/org/libreplan/business/templates/entities/OrderElementTemplate.java @@ -354,14 +354,6 @@ public abstract class OrderElementTemplate extends BaseEntity implements this.deadlineAsDaysFromBeginning = days; } - public String getCode() { - return getInfoComponent().getCode(); - } - - public void setCode(String code) { - getInfoComponent().setCode(code); - } - public String getDescription() { return getInfoComponent().getDescription(); } @@ -384,7 +376,6 @@ public abstract class OrderElementTemplate extends BaseEntity implements } protected void copyTo(OrderElementTemplate result) { - result.setCode(getCode()); result.setName(getName()); result.setDescription(getDescription()); result.setDeadlineAsDaysFromBeginning(getDeadlineAsDaysFromBeginning()); diff --git a/libreplan-business/src/main/resources/db.changelog-1.2.xml b/libreplan-business/src/main/resources/db.changelog-1.2.xml index 1bdd6ac04..e9d3dc9a2 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.2.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.2.xml @@ -186,12 +186,14 @@ columnDataType="BOOLEAN" /> - + Change column description in order_element to TEXT - + Change column description in order_element_template to TEXT @@ -223,6 +225,18 @@ + + + Change column description in order_element and + order_element_template to TEXT in MySQL. + Because of using modifyDataType convert the column in LONGTEXT and + this is causing some problems with Hibernate mapping. + + ALTER TABLE order_element MODIFY description TEXT + ALTER TABLE order_element_template MODIFY description TEXT + + add budget column to order_line @@ -318,4 +332,31 @@ + + Add new columns for currency in configuration table + + + + + + + + + + + + + + Remove column code in order_element_template table + + + diff --git a/libreplan-business/src/main/resources/libreplan-business-spring-config.xml b/libreplan-business/src/main/resources/libreplan-business-spring-config.xml index 732afe25c..6eae552c4 100644 --- a/libreplan-business/src/main/resources/libreplan-business-spring-config.xml +++ b/libreplan-business/src/main/resources/libreplan-business-spring-config.xml @@ -144,7 +144,10 @@ factory-method="getInstance" lazy-init="false"> - ${default.passwordsControl} + ${default.passwordsControl} + + + ${default.exampleUsersDisabled} diff --git a/libreplan-business/src/main/resources/org/libreplan/business/common/entities/Configuration.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/common/entities/Configuration.hbm.xml index d39b350c9..0e6822cd1 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/common/entities/Configuration.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/common/entities/Configuration.hbm.xml @@ -66,6 +66,9 @@ + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml index 43ea6eec8..309dcc5db 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml @@ -8,7 +8,7 @@ - + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/templates/entities/Templates.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/templates/entities/Templates.hbm.xml index 04ee58d8c..f64091ca5 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/templates/entities/Templates.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/templates/entities/Templates.hbm.xml @@ -12,7 +12,6 @@ - diff --git a/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/GenericResourceAllocationTest.java b/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/GenericResourceAllocationTest.java index 135956ede..780243ad1 100644 --- a/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/GenericResourceAllocationTest.java +++ b/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/GenericResourceAllocationTest.java @@ -22,7 +22,6 @@ package org.libreplan.business.test.planner.entities; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.getCurrentArguments; import static org.easymock.EasyMock.isA; @@ -778,7 +777,7 @@ public class GenericResourceAllocationTest { givenGenericResourceAllocationForTask(task); givenWorkersWithLoads(8, 6, 2); IntraDayDate end = ResourceAllocation.allocating( - singletonList(ResourcesPerDayModification.create( + Arrays.asList(ResourcesPerDayModification.create( genericResourceAllocation, ResourcesPerDay.amount(new BigDecimal(1)), workers))) .untilAllocating(hours(12)); @@ -798,7 +797,7 @@ public class GenericResourceAllocationTest { givenGenericResourceAllocationForTask(task); givenWorkersWithLoads(8, 2, 6); IntraDayDate end = ResourceAllocation.allocating( - singletonList(ResourcesPerDayModification.create( + Arrays.asList(ResourcesPerDayModification.create( genericResourceAllocation, ResourcesPerDay.amount(1), workers))).untilAllocating(hours(16)); assertThat(end.getDate(), equalTo(start.plusDays(2))); diff --git a/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/UntilFillingHoursAllocatorTest.java b/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/UntilFillingHoursAllocatorTest.java index a6dc823a8..416390ee5 100644 --- a/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/UntilFillingHoursAllocatorTest.java +++ b/libreplan-business/src/test/java/org/libreplan/business/test/planner/entities/UntilFillingHoursAllocatorTest.java @@ -412,12 +412,11 @@ public class UntilFillingHoursAllocatorTest { givenSpecificAllocations(ResourcesPerDay.amount(1), ResourcesPerDay .amount(1)); ResourceAllocation.allocating(allocations).untilAllocating(hours(64)); - ResourceAllocation generic = allocations.get(0) + ResourceAllocation firstSpecific = allocations.get(0) .getBeingModified(); - ResourceAllocation firstSpecific = allocations.get(1) - .getBeingModified(); - ResourceAllocation secondSpecific = allocations.get(2) + ResourceAllocation secondSpecific = allocations.get(1) .getBeingModified(); + ResourceAllocation generic = allocations.get(2).getBeingModified(); assertThat(generic.getAssignments(), haveHours(16, 16)); assertThat(firstSpecific.getAssignments(), haveHours(8, 8)); assertThat(secondSpecific.getAssignments(), haveHours(8, 8)); diff --git a/libreplan-webapp/pom.xml b/libreplan-webapp/pom.xml index ce21785ca..3d105835a 100644 --- a/libreplan-webapp/pom.xml +++ b/libreplan-webapp/pom.xml @@ -4,7 +4,7 @@ org.libreplan libreplan - 1.2.2 + 1.2.3 libreplan-webapp war @@ -126,6 +126,9 @@ + + +
@@ -434,5 +437,10 @@ org.jgrapht jgrapht-jdk1.5 + + + br.com.digilabs.jqplot + jqplot4java + diff --git a/libreplan-webapp/src/main/jasper/completedEstimatedHours_Bundle/completedEstimatedHours_pl.properties b/libreplan-webapp/src/main/jasper/completedEstimatedHours_Bundle/completedEstimatedHours_pl.properties new file mode 100644 index 000000000..0da7759e5 --- /dev/null +++ b/libreplan-webapp/src/main/jasper/completedEstimatedHours_Bundle/completedEstimatedHours_pl.properties @@ -0,0 +1,16 @@ +# Locale for completedEstimatedHours.jrxml +title = Raport godzin +subtitle = Zrealizowane szacowane godziny +order = Projekt \: +reference = Data referencyjna \: +criteria = Kryteria \: +labels = Etykiety \: +headers.column1 = Nazwa +headers.column2 = Wszystkie godziny +headers.column2.column1 = Szacowany +headers.column2.column2 = Planowany +headers.column3 = Godziny do tej pory +headers.column3.column1 = Planowany +headers.column3.column2 = Realny +page = strona +of = z diff --git a/libreplan-webapp/src/main/jasper/hoursWorkedPerWorkerInAMonth_Bundle/hoursWorkedPerWorkerInAMonth_pl.properties b/libreplan-webapp/src/main/jasper/hoursWorkedPerWorkerInAMonth_Bundle/hoursWorkedPerWorkerInAMonth_pl.properties new file mode 100644 index 000000000..c13d368af --- /dev/null +++ b/libreplan-webapp/src/main/jasper/hoursWorkedPerWorkerInAMonth_Bundle/hoursWorkedPerWorkerInAMonth_pl.properties @@ -0,0 +1,11 @@ +# Locale for hoursWorkedPerWorkerReportInAMonth.jrxml +title = Raport pracy +subtitle = Raport pracy wed\u0142ug zasobw w miesi\u0105cu +parameters.year = Rok\: +parameters.month = Miesi\u0105c\: +headers.column1 = Nazwa +headers.column2 = Godziny +total.hours = Wszystkie godziny\: +note1 = Brak raportw pracy dla zaznaczonych zasobw w tym zakresie wyszukiwania. +page = strona +of = z diff --git a/libreplan-webapp/src/main/jasper/hoursWorkedPerWorkerReport.jrxml b/libreplan-webapp/src/main/jasper/hoursWorkedPerWorkerReport.jrxml index 707ed3853..ddfa9c325 100644 --- a/libreplan-webapp/src/main/jasper/hoursWorkedPerWorkerReport.jrxml +++ b/libreplan-webapp/src/main/jasper/hoursWorkedPerWorkerReport.jrxml @@ -9,8 +9,8 @@ - - + + @@ -343,7 +343,7 @@ - + @@ -355,7 +355,7 @@ - + diff --git a/libreplan-webapp/src/main/jasper/hoursWorkedPerWorker_Bundle/hoursWorkedPerWorker_pl.properties b/libreplan-webapp/src/main/jasper/hoursWorkedPerWorker_Bundle/hoursWorkedPerWorker_pl.properties new file mode 100644 index 000000000..b110c3cbc --- /dev/null +++ b/libreplan-webapp/src/main/jasper/hoursWorkedPerWorker_Bundle/hoursWorkedPerWorker_pl.properties @@ -0,0 +1,18 @@ +# Locale for hoursWorkedPerWorkerReport.jrxml +title = Raport pracy +subtitle = Przepracowane godziny wed\u0142ug zasobw +date.start = Data rozpocz\u0119cia\: +date.end = Data zako\u0144czenia\: +criteria = Kryteria\: +labels = Etykiety\: +headers.column1 = Start +headers.column2 = Zako\u0144czenie +headers.column3 = Godziny +headers.column4 = Kod/nazwa zadania +headers.column5 = Pola tekstowe +headers.column6 = Etykiety +total.day = Wszystkie godziny wed\u0142ug dnia\: +total.worker = Wszystkie godziny wed\u0142ug pracownika\: +note1 = Nie ma raportw pracy dla zaznaczonych zasobw w zakresie wyszukiwania. +page = strona +of = z diff --git a/libreplan-webapp/src/main/jasper/orderCostsPerResourceReport.jrxml b/libreplan-webapp/src/main/jasper/orderCostsPerResourceReport.jrxml index 7288e1dad..3e3da7e51 100644 --- a/libreplan-webapp/src/main/jasper/orderCostsPerResourceReport.jrxml +++ b/libreplan-webapp/src/main/jasper/orderCostsPerResourceReport.jrxml @@ -7,10 +7,9 @@ + - - @@ -90,12 +89,12 @@ - + - + @@ -137,10 +136,10 @@ - + - + @@ -230,10 +229,10 @@ - + - + @@ -266,20 +265,20 @@ - + - + - + - + diff --git a/libreplan-webapp/src/main/jasper/orderCostsPerResource_Bundle/orderCostsPerResource_es.properties b/libreplan-webapp/src/main/jasper/orderCostsPerResource_Bundle/orderCostsPerResource_es.properties index 58467adeb..3f47ebdd0 100644 --- a/libreplan-webapp/src/main/jasper/orderCostsPerResource_Bundle/orderCostsPerResource_es.properties +++ b/libreplan-webapp/src/main/jasper/orderCostsPerResource_Bundle/orderCostsPerResource_es.properties @@ -1,17 +1,17 @@ # Locale for ordersCostPerResourcesReport.jrxml -title = Project report -subtitle = Costs by Resource -date.start = Starting date: -date.end = Ending date: -criteria = Criteria : -labels = Labels : -headers.column1 = Type of hours -headers.column2 = Date -headers.column3 = Salary -headers.column4 = Hours -headers.column5 = Costs -total.task = Total per task: -total.worker = Total per worker: -total = Project Total: -page = page -of = of +title = Informe del proyecto +subtitle = Costes por recurso +date.start = Fecha de inicio: +date.end = Fecha de fin: +criteria = Criterios: +labels = Etiquetas: +headers.column1 = Tipo de horas +headers.column2 = Fecha +headers.column3 = Salario +headers.column4 = Horas +headers.column5 = Costes +total.task = Total por tarea: +total.worker = Total por trabajador: +total = Total por proyecto: +page = página +of = de diff --git a/libreplan-webapp/src/main/jasper/orderCostsPerResource_Bundle/orderCostsPerResource_pl.properties b/libreplan-webapp/src/main/jasper/orderCostsPerResource_Bundle/orderCostsPerResource_pl.properties new file mode 100644 index 000000000..ebfe76e03 --- /dev/null +++ b/libreplan-webapp/src/main/jasper/orderCostsPerResource_Bundle/orderCostsPerResource_pl.properties @@ -0,0 +1,17 @@ +# Locale for ordersCostPerResourcesReport.jrxml +title = Raport projektu +subtitle = Koszty wed\u0142ug zasobu +date.start = Data rozpocz\u0119cia\: +date.end = Data zako\u0144czenia\: +criteria = Kryteria \: +labels = Etykiety \: +headers.column1 = Typ godzin +headers.column2 = Data +headers.column3 = Wynagrodzenie +headers.column4 = Godziny +headers.column5 = Koszty +total.task = Og\u0142em na dzie\u0144\: +total.worker = Og\u0142em na pracownika\: +total = Ca\u0142kowity projekt\: +page = strona +of = z diff --git a/libreplan-webapp/src/main/jasper/schedulingProgressPerOrder_Bundle/schedulingProgressPerOrder_pl.properties b/libreplan-webapp/src/main/jasper/schedulingProgressPerOrder_Bundle/schedulingProgressPerOrder_pl.properties new file mode 100644 index 000000000..316f52611 --- /dev/null +++ b/libreplan-webapp/src/main/jasper/schedulingProgressPerOrder_Bundle/schedulingProgressPerOrder_pl.properties @@ -0,0 +1,27 @@ +# Locale for schedulingProgressPerOrderReport.jrxml +title = Raport post\u0119pu +subtitle = Harmonogram post\u0119pu na projekt +tipo = Typ post\u0119pu\: +order = Projekty\: +date.start = Data rozpocz\u0119cia. +date.end = Data zako\u0144czenia\: +date.reference = Data referencyjna\: +headers.column1 = Wszystkie godziny +headers.column1.column1 = Szacowany +headers.column1.column2 = Planowany +headers.column2 = Post\u0119p +headers.column2.column1 = Mierzony +headers.column2.column2 = Kalkulacja +headers.column2.column3 = Planowany +headers.column3 = Godziny do tej pory +headers.column3.column1 = Planowany +headers.column3.column2 = Realny +headers.column4 = R\u017cnica +headers.column4.column1 = Koszt +headers.column4.column2 = Planowany +headers.column4.column3 = Wska\u017anik kosztw +headers.column4.column4 = Planowany wska\u017anik +note1 = Zaznaczony typ post\u0119pu nie jest dost\u0119pny dla tego projektu. +note2 = U\u017cywany rozpowszechniany typ procesu. +page = strona +of = z diff --git a/libreplan-webapp/src/main/jasper/timeLineRequiredMaterial.jrxml b/libreplan-webapp/src/main/jasper/timeLineRequiredMaterial.jrxml index b50aed032..894dd89dc 100644 --- a/libreplan-webapp/src/main/jasper/timeLineRequiredMaterial.jrxml +++ b/libreplan-webapp/src/main/jasper/timeLineRequiredMaterial.jrxml @@ -5,6 +5,7 @@ + @@ -156,7 +157,7 @@ - + @@ -291,7 +292,7 @@ - + @@ -303,7 +304,7 @@ - + diff --git a/libreplan-webapp/src/main/jasper/timeLineRequiredMaterial_Bundle/timeLineRequiredMaterial_pl.properties b/libreplan-webapp/src/main/jasper/timeLineRequiredMaterial_Bundle/timeLineRequiredMaterial_pl.properties new file mode 100644 index 000000000..a5f584a47 --- /dev/null +++ b/libreplan-webapp/src/main/jasper/timeLineRequiredMaterial_Bundle/timeLineRequiredMaterial_pl.properties @@ -0,0 +1,17 @@ +# Locale for timeLineRequiredMaterial.jrxml +title = Materia\u0142y +subtitle = Linia czasowa materia\u0142w. +date.start = Data rozpocz\u0119cia\: +date.end = Data zako\u0144czenia\: +status = Status\: +headers.title = Dzie\u0144 +headers.column1 = Materia\u0142 +headers.column2 = Projekt - Zadanie +headers.column3 = Dost\u0119pno\u015b\u0107 +headers.column4 = Jednostki +headers.column5 = Cena jednostkowa +headers.column6 = Cena +headers.column7 = Status +headers.total = Ca\u0142kowita cena za dzie\u0144\: +page = strona +of = z diff --git a/libreplan-webapp/src/main/jasper/workingArrangementsPerOrder_Bundle/workingArrangementsPerOrder_pl.properties b/libreplan-webapp/src/main/jasper/workingArrangementsPerOrder_Bundle/workingArrangementsPerOrder_pl.properties new file mode 100644 index 000000000..9425a2605 --- /dev/null +++ b/libreplan-webapp/src/main/jasper/workingArrangementsPerOrder_Bundle/workingArrangementsPerOrder_pl.properties @@ -0,0 +1,28 @@ +# Locale for workingArrangementPerOrderReport.jrxml +title = Raport projektu +subtitle = Stan planowania zada\u0144. +order = Projekt\: +status = Status\: +name = Nazwa. +code = Kod\: +criteria = Kryteria \: +labels = Etykiety \: +headers1.column1 = Data rozpocz\u0119cia +headers1.column1.column1 = Szacowany +headers1.column1.column2 = Pierwszy raport +headers1.column2 = Data zako\u0144czenia +headers1.column2.column1 = Szacowany +headers1.column2.column2 = Ostatni raport +headers1.column2.column3 = Ostateczny termin +headers1.column3 = Post\u0119p +headers1.column4 = Status +headers1.column5 = Ostateczny termin +headers2.title = Zale\u017cno\u015bci. +headers2.column1 = Nazwa +headers2.column2 = Kod +headers2.column3 = Typ +headers2.column4 = Post\u0119p +overrun = przekroczenia +intime = W czasie. +page = strona +of = z diff --git a/libreplan-webapp/src/main/jasper/workingProgressPerTask_Bundle/workingProgressPerTask_pl.properties b/libreplan-webapp/src/main/jasper/workingProgressPerTask_Bundle/workingProgressPerTask_pl.properties new file mode 100644 index 000000000..74ebd6c52 --- /dev/null +++ b/libreplan-webapp/src/main/jasper/workingProgressPerTask_Bundle/workingProgressPerTask_pl.properties @@ -0,0 +1,24 @@ +# Locale for workingProgressPerTaskReport.jrxml +title = Raport post\u0119pu +subtitle = Post\u0119p pracy wed\u0142ug zadania +order = Projekt +reference = Data odniesienia\: +criteria = Kryteria \: +labels = Etykiety \: +headers.column1 = Wszystkie godziny +headers.column1.column1 = Szacowany +headers.column1.column2 = Planowany +headers.column2 = Post\u0119p +headers.column2.column1 = Mierzony +headers.column2.column2 = Kalkulacja +headers.column2.column3 = Planowany +headers.column3 = Godziny do tej pory +headers.column3.column1 = Planowany +headers.column3.column2 = Realny +headers.column4 = R\u017cnica +headers.column4.column1 = Koszt +headers.column4.column2 = Planowany +headers.column4.column3 = Wska\u017anik kosztw +headers.column4.column4 = Planowany wska\u017anik +page = strona +of = z diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java index d26a1816f..ce6dc1795 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -836,4 +836,28 @@ public class ConfigurationController extends GenericForwardComposer { .setAllowToGatherUsageStatsEnabled(allowToGatherUsageStatsEnabled); } + public Set getCurrencies() { + return configurationModel.getCurrencies(); + } + + public ListitemRenderer getCurrencyRenderer() { + return new ListitemRenderer() { + @Override + public void render(Listitem item, Object data) throws Exception { + String currencyCode = (String) data; + item.setLabel(currencyCode + " - " + + configurationModel.getCurrencySymbol(currencyCode)); + item.setValue(currencyCode); + } + }; + } + + public String getSelectedCurrency() { + return configurationModel.getCurrencyCode(); + } + + public void setSelectedCurrency(String currencyCode) { + configurationModel.setCurrency(currencyCode); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java index d98576983..1cc6e432a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -25,12 +25,16 @@ import static org.libreplan.web.I18nHelper._; import java.util.ArrayList; import java.util.Collection; +import java.util.Currency; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.TreeMap; +import org.apache.commons.lang.StringUtils; import org.libreplan.business.calendars.daos.IBaseCalendarDAO; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.daos.IConfigurationDAO; @@ -66,6 +70,8 @@ public class ConfigurationModel implements IConfigurationModel { private Map> entitySequences = new HashMap>(); + private static Map currencies = getAllCurrencies(); + @Autowired private IConfigurationDAO configurationDAO; @@ -557,4 +563,39 @@ public class ConfigurationModel implements IConfigurationModel { .setAllowToGatherUsageStatsEnabled(allowToGatherUsageStatsEnabled); } + private static Map getAllCurrencies() { + Map currencies = new TreeMap(); + for (Locale locale : Locale.getAvailableLocales()) { + if (StringUtils.isNotBlank(locale.getCountry())) { + Currency currency = Currency.getInstance(locale); + currencies.put(currency.getCurrencyCode(), + currency.getSymbol(locale)); + } + } + return currencies; + } + + @Override + public Set getCurrencies() { + return currencies.keySet(); + } + + @Override + public String getCurrencySymbol(String currencyCode) { + return currencies.get(currencyCode); + } + + @Override + public String getCurrencyCode() { + return configuration.getCurrencyCode(); + } + + @Override + public void setCurrency(String currencyCode) { + if (configuration != null) { + configuration.setCurrencyCode(currencyCode); + configuration.setCurrencySymbol(currencies.get(currencyCode)); + } + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java index 7c1a7453b..12a6bcc68 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -22,6 +22,7 @@ package org.libreplan.web.common; import java.util.List; +import java.util.Set; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.entities.EntityNameEnum; @@ -153,4 +154,13 @@ public interface IConfigurationModel { void setAllowToGatherUsageStatsEnabled( boolean allowToGatherUsageStatsEnabled); + + Set getCurrencies(); + + String getCurrencySymbol(String currencyCode); + + String getCurrencyCode(); + + void setCurrency(String currencyCode); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/IntegrationEntityModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/IntegrationEntityModel.java index f96c08b53..dd780cea7 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/IntegrationEntityModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IntegrationEntityModel.java @@ -24,7 +24,6 @@ package org.libreplan.web.common; import static org.libreplan.web.I18nHelper._; import java.util.ConcurrentModificationException; -import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -125,9 +124,4 @@ public abstract class IntegrationEntityModel implements IIntegrationEntityModel return entitySequenceDAO; } - public Date getCurrentExpiringDate() { - // TODO Auto-generated method stub - return null; - } - } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java index 09bf5de3a..9f7f7765e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java @@ -482,7 +482,11 @@ public class TemplateModel implements ITemplateModel { @Override @Transactional(readOnly = true) public boolean isUserAdmin() { - return UserUtil.getUserFromSession().isAdministrator(); + User user = UserUtil.getUserFromSession(); + if(user == null) { + return false; + } + return user.isAdministrator(); } @Override diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java index cc4b29942..9cf2d05b5 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java @@ -24,6 +24,7 @@ package org.libreplan.web.common; import static org.libreplan.web.I18nHelper._; import java.math.BigDecimal; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; @@ -31,6 +32,9 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.libreplan.business.common.Configuration; +import org.libreplan.business.common.IOnTransaction; +import org.libreplan.business.common.Registry; import org.zkoss.ganttz.util.ComponentsFinder; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; @@ -62,6 +66,12 @@ public class Util { private static final Log LOG = LogFactory.getLog(Util.class); + /** + * Special chars from {@link DecimalFormat} class. + */ + private static final String[] DECIMAL_FORMAT_SPECIAL_CHARS = { "0", ",", + ".", "\u2030", "%", "#", ";", "-" }; + private Util() { } @@ -629,4 +639,55 @@ public class Util { } } + /** + * Gets currency symbol from {@link Configuration} object. + * + * @return Currency symbol configured in the application + */ + public static String getCurrencySymbol() { + return Registry.getTransactionService().runOnReadOnlyTransaction( + new IOnTransaction() { + @Override + public String execute() { + return Registry.getConfigurationDAO() + .getConfiguration().getCurrencySymbol(); + } + }); + } + + /** + * Returns the value using the money format, that means, 2 figures for the + * decimal part and concatenating the currency symbol from + * {@link Configuration} object. + */ + public static String addCurrencySymbol(BigDecimal value) { + DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat + .getInstance(); + decimalFormat.applyPattern(getMoneyFormat()); + return decimalFormat.format(value); + } + + /** + * Gets money format for a {@link Decimalbox} using 2 figures for the + * decimal part and concatenating the currency symbol + * + * @return Format for a {@link Decimalbox} ###.## plus currency + * symbol + */ + public static String getMoneyFormat() { + return "###.## " + escapeDecimalFormatSpecialChars(getCurrencySymbol()); + } + + /** + * Escapes special chars used in {@link DecimalFormat} to define the number + * format that appear in the currencySymbol. + */ + private static String escapeDecimalFormatSpecialChars(String currencySymbol) { + for (String specialChar : DECIMAL_FORMAT_SPECIAL_CHARS) { + currencySymbol = currencySymbol.replace(specialChar, "'" + + specialChar + "'"); + } + return currencySymbol; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/bandboxsearch/BandboxMultipleSearch.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/bandboxsearch/BandboxMultipleSearch.java index 96d83463e..435bd97dd 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/bandboxsearch/BandboxMultipleSearch.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/bandboxsearch/BandboxMultipleSearch.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -53,9 +53,15 @@ import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.api.Listbox; /** + * {@link Bandbox} allowing to choose more than one element from the drop down + * list separating them by ";".
+ * + * When an element is selected {@link Events.ON_CHANGE} event over this + * component is launched. + * * @author Susana Montes Pedreira + * @author Manuel Rego Casasnovas */ - @SuppressWarnings("serial") public class BandboxMultipleSearch extends HtmlMacroComponent { @@ -168,6 +174,7 @@ public class BandboxMultipleSearch extends HtmlMacroComponent { } } bandbox.close(); + Events.postEvent(Events.ON_CHANGE, this, null); } private void searchMultipleFilters() { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourceFilterEnumByResourceAndCriterion.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourceFilterEnumByResourceAndCriterion.java new file mode 100644 index 000000000..13e752167 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourceFilterEnumByResourceAndCriterion.java @@ -0,0 +1,50 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.web.common.components.finders; + +import org.libreplan.business.resources.entities.Resource; + +/** + * Diferent filters for {@link Resource}. + * + * @author Manuel Rego Casasnovas + */ +public enum ResourceFilterEnumByResourceAndCriterion implements IFilterEnum { + + RESOURCE(_("Resource")), CRITERION(_("Criterion")); + + /** + * Forces to mark the string as needing translation + */ + private static String _(String string) { + return string; + } + + private String description; + + private ResourceFilterEnumByResourceAndCriterion(String description) { + this.description = description; + } + + public String toString() { + return this.description; + } + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourceMultipleFiltersFinderByResourceAndCriterion.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourceMultipleFiltersFinderByResourceAndCriterion.java new file mode 100644 index 000000000..82b93706f --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourceMultipleFiltersFinderByResourceAndCriterion.java @@ -0,0 +1,184 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * Copyright (C) 2010-2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.web.common.components.finders; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; + +import org.apache.commons.lang.StringUtils; +import org.libreplan.business.hibernate.notification.PredefinedDatabaseSnapshots; +import org.libreplan.business.resources.entities.Criterion; +import org.libreplan.business.resources.entities.CriterionType; +import org.libreplan.business.resources.entities.Resource; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implements all the methods needed to search the different criteria to filter + * the {@link Resource}s.
+ * It provides the following criteria to filter: {@link Resource} and + * {@link Criterion}. + * + * @author Manuel Rego Casasnovas + */ +public class ResourceMultipleFiltersFinderByResourceAndCriterion extends MultipleFiltersFinder { + + @Autowired + private PredefinedDatabaseSnapshots databaseSnapshots; + + protected ResourceMultipleFiltersFinderByResourceAndCriterion() { + } + + @Override + public List getFirstTenFilters() { + getListMatching().clear(); + fillWithFirstTenFiltersResources(); + fillWithFirstTenFiltersCriterions(); + addNoneFilter(); + return getListMatching(); + } + + private List fillWithFirstTenFiltersResources() { + Map, List> mapResources = getMapResources(); + Iterator> iteratorClass = mapResources.keySet().iterator(); + while (iteratorClass.hasNext() && getListMatching().size() < 10) { + Class className = iteratorClass.next(); + for (int i = 0; getListMatching().size() < 10 + && i < mapResources.get(className).size(); i++) { + Resource resource = mapResources.get(className).get(i); + addResource(className, resource); + } + } + return getListMatching(); + } + + private Map, List> getMapResources() { + return databaseSnapshots.snapshotMapResources(); + } + + private void addResource(Class className, Resource resource) { + String pattern = resource.getName(); + getListMatching().add( + new FilterPair(ResourceFilterEnumByResourceAndCriterion.RESOURCE, className + .getSimpleName(), pattern, resource)); + } + + private List fillWithFirstTenFiltersCriterions() { + SortedMap> mapCriterions = getMapCriterions(); + Iterator iteratorCriterionType = mapCriterions.keySet() + .iterator(); + while (iteratorCriterionType.hasNext() && getListMatching().size() < 10) { + CriterionType type = iteratorCriterionType.next(); + for (int i = 0; getListMatching().size() < 10 + && i < mapCriterions.get(type).size(); i++) { + Criterion criterion = mapCriterions.get(type).get(i); + addCriterion(type, criterion); + } + } + return getListMatching(); + } + + private SortedMap> getMapCriterions() { + return databaseSnapshots.snapshotCriterionsMap(); + } + + private void addCriterion(CriterionType type, Criterion criterion) { + String pattern = criterion.getName() + " ( " + type.getName() + " )"; + getListMatching().add( + new FilterPair(ResourceFilterEnumByResourceAndCriterion.CRITERION, type + .getResource().toLowerCase(), pattern, criterion)); + } + + public List getMatching(String filter) { + getListMatching().clear(); + if ((filter != null) && (!filter.isEmpty())) { + filter = StringUtils.deleteWhitespace(filter.toLowerCase()); + searchInResources(filter); + searchInCriterionTypes(filter); + } + + addNoneFilter(); + return getListMatching(); + } + + private void searchInResources(String filter) { + Map, List> mapResources = databaseSnapshots + .snapshotMapResources(); + for (Class className : mapResources.keySet()) { + for (Resource resource : mapResources.get(className)) { + String name = StringUtils.deleteWhitespace(resource.getName() + .toLowerCase()); + if (name.contains(filter)) { + addResource(className, resource); + if ((filter.length() < 3) && (getListMatching().size() > 9)) { + return; + } + } + } + } + } + + private void searchInCriterionTypes(String filter) { + boolean limited = (filter.length() < 3); + for (CriterionType type : getMapCriterions().keySet()) { + String name = StringUtils.deleteWhitespace(type.getName() + .toLowerCase()); + if (name.contains(filter)) { + setFilterPairCriterionType(type, limited); + } else { + searchInCriterions(type, filter); + } + } + } + + private void searchInCriterions(CriterionType type, String filter) { + List list = getMapCriterions().get(type); + if (list == null) { + return; + } + for (Criterion criterion : list) { + String name = StringUtils.deleteWhitespace(criterion.getName() + .toLowerCase()); + if (name.contains(filter)) { + addCriterion(type, criterion); + if ((filter.length() < 3) && (getListMatching().size() > 9)) { + return; + } + } + } + } + + private void setFilterPairCriterionType(CriterionType type, boolean limited) { + List list = getMapCriterions().get(type); + if (list == null) { + return; + } + for (Criterion criterion : list) { + addCriterion(type, criterion); + if ((limited) && (getListMatching().size() > 9)) { + return; + } + } + } + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourcesMultipleFiltersFinder.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourcesMultipleFiltersFinder.java index be6746e45..25579c12c 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourcesMultipleFiltersFinder.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/ResourcesMultipleFiltersFinder.java @@ -35,11 +35,11 @@ import org.springframework.beans.factory.annotation.Autowired; /** * Implements all the methods needed to search the criterion to filter the - * resources. Provides multiples criterions to filter like {@link Criterion}, - * {@link Category} or filter by name or nif. + * resources. Provides multiples criteria to filter like {@link Criterion} or + * {@link CostCategory}. + * * @author Susana Montes Pedreira */ - public class ResourcesMultipleFiltersFinder extends MultipleFiltersFinder { @Autowired diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/TemplateFinder.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/TemplateFinder.java index 92d69663b..f4eec6d6e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/TemplateFinder.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/TemplateFinder.java @@ -79,7 +79,7 @@ public abstract class TemplateFinder extends } protected String extractStringFor(T template) { - return template.getName() + " (" + template.getCode() + ")"; + return template.getName(); } @Override @@ -92,7 +92,6 @@ public abstract class TemplateFinder extends protected void generateColumnsForRenderer(Listitem item, T template) { final Listcell codeCell = new Listcell(); - codeCell.setLabel(template.getCode()); codeCell.setParent(item); codeCell.setStyle("width:200px"); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/WorkerMultipleFiltersFinder.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/WorkerMultipleFiltersFinder.java deleted file mode 100644 index a10f92f6b..000000000 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/finders/WorkerMultipleFiltersFinder.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of LibrePlan - * - * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e - * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. - * - * 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 Affero 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 . - */ - -package org.libreplan.web.common.components.finders; - -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.lang.StringUtils; -import org.libreplan.business.hibernate.notification.PredefinedDatabaseSnapshots; -import org.libreplan.business.resources.entities.Worker; -import org.springframework.beans.factory.annotation.Autowired; - -public class WorkerMultipleFiltersFinder extends MultipleFiltersFinder { - - @Autowired - private PredefinedDatabaseSnapshots databaseSnapshots; - - private IFilterEnum workerFilterEnum = new IFilterEnum() { - @Override - public String toString() { - return "Resource ( Worker )"; - } - }; - - /** - * Forces to mark the string as needing translation - */ - private static String _(String string) { - return string; - } - - @Override - public List getFirstTenFilters() { - getListMatching().clear(); - Iterator iteratorWorker = getListWorkers().iterator(); - while(iteratorWorker.hasNext() && getListMatching().size() < 10) { - Worker worker = iteratorWorker.next(); - getListMatching().add( - new FilterPair(workerFilterEnum, worker.getDescription(), - worker)); - } - addNoneFilter(); - return getListMatching(); - } - - private List getListWorkers() { - return databaseSnapshots.snapshotListWorkers(); - } - - @Override - public List getMatching(String filter) { - getListMatching().clear(); - if ((filter != null) && (!filter.isEmpty())) { - filter = StringUtils.deleteWhitespace(filter.toLowerCase()); - searchInWorkers(filter); - } - addNoneFilter(); - return getListMatching(); - - } - private void searchInWorkers(String filter) { - for (Worker worker : getListWorkers()) { - String name = StringUtils.deleteWhitespace(worker.getDescription() - .toLowerCase()); - if(name.contains(filter)) { - getListMatching().add(new FilterPair( - workerFilterEnum, worker.getShortDescription(), worker)); - } - } - } - -} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/CostCategoryCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/CostCategoryCRUDController.java index cfdd24218..fc560ddec 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/CostCategoryCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/CostCategoryCRUDController.java @@ -38,10 +38,8 @@ import org.libreplan.business.costcategories.entities.CostCategory; import org.libreplan.business.costcategories.entities.HourCost; import org.libreplan.business.costcategories.entities.TypeOfWorkHours; import org.libreplan.web.common.BaseCRUDController; -import org.libreplan.web.common.ConstraintChecker; import org.libreplan.web.common.Level; import org.libreplan.web.common.Util; -import org.libreplan.web.util.ValidationExceptionPrinter; import org.libreplan.web.workreports.WorkReportCRUDController; import org.zkoss.ganttz.util.ComponentsFinder; import org.zkoss.zk.ui.Component; @@ -305,6 +303,7 @@ public class CostCategoryCRUDController extends BaseCRUDController Decimalbox boxCost = new Decimalbox(); bindDecimalboxCost(boxCost, (HourCost) row.getValue()); boxCost.setConstraint("no empty:" + _("cannot be null or empty")); + boxCost.setFormat(Util.getMoneyFormat()); row.appendChild(boxCost); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/TypeOfWorkHoursCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/TypeOfWorkHoursCRUDController.java index 4eb5cf3d2..814fce5ad 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/TypeOfWorkHoursCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/costcategories/TypeOfWorkHoursCRUDController.java @@ -141,4 +141,13 @@ public class TypeOfWorkHoursCRUDController extends BaseCRUDController. + */ + +package org.libreplan.web.dashboard; + +import static org.libreplan.web.I18nHelper._; + +import java.math.BigDecimal; +import java.util.Date; + +import org.joda.time.LocalDate; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.web.common.Util; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Label; + +/** + * @author Diego Pino García + * + * Contains operations for calculations in the CostStatus table in the + * Dashboard view + */ +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class CostStatusController extends GenericForwardComposer { + + private ICostStatusModel costStatusModel; + + // Cost Variance + public Label lblCV; + + // Cost Performance Index + public Label lblCPI; + + // Budget at Completion + public Label lblBAC; + + // Estimate at Completion + public Label lblEAC; + + // Variance at Completion + public Label lblVAC; + + @Override + public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { + super.doAfterCompose(comp); + self.setAttribute("controller", this); + Util.createBindingsFor(self); + } + + public void setOrder(Order order) { + costStatusModel.setCurrentOrder(order); + } + + public void render() { + LocalDate today = LocalDate.fromDateFields(new Date()); + BigDecimal budgetedCost = costStatusModel + .getBudgetedCostWorkPerformedAt(today); + BigDecimal actualCost = costStatusModel + .getActualCostWorkPerformedAt(today); + + BigDecimal costVariance = costStatusModel.getCostVariance(budgetedCost, + actualCost); + setCostVariance(costVariance); + + BigDecimal costPerformanceIndex = costStatusModel + .getCostPerformanceIndex(budgetedCost, actualCost); + setCostPerformanceIndex(costPerformanceIndex); + + BigDecimal budgetAtCompletion = costStatusModel.getBudgetAtCompletion(); + setBudgetAtCompletion(budgetAtCompletion); + + BigDecimal estimateAtCompletion = costStatusModel + .getEstimateAtCompletion(budgetAtCompletion, + costPerformanceIndex); + setEstimateAtCompletion(estimateAtCompletion); + + BigDecimal varianceAtCompletion = costStatusModel + .getVarianceAtCompletion(budgetAtCompletion, + estimateAtCompletion); + setVarianceAtCompletion(varianceAtCompletion); + } + + private void setEstimateAtCompletion(BigDecimal value) { + lblEAC.setValue(String.format("%.2f %%", value.doubleValue())); + } + + private void setCostPerformanceIndex(BigDecimal value) { + lblCPI.setValue(String.format("%.2f %%", value.doubleValue())); + } + + private void setBudgetAtCompletion(BigDecimal value) { + lblBAC.setValue(String.format(_("%s h"), value.toString())); + } + + private void setCostVariance(BigDecimal value) { + lblCV.setValue(String.format(_("%s h"), value.toString())); + } + + private void setVarianceAtCompletion(BigDecimal value) { + lblVAC.setValue(String.format(_("%s h"), value.toString())); + } + +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/CostStatusModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/CostStatusModel.java new file mode 100644 index 000000000..838cf8629 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/CostStatusModel.java @@ -0,0 +1,107 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2010-2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.web.dashboard; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.planner.entities.IOrderEarnedValueCalculator; +import org.libreplan.web.planner.order.OrderPlanningModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Diego Pino García + * + * Model for UI operations related to CostStatus in Dashboard view + * + * FIXME: This Model contains several operations for calculating 'Earned + * Value' measures related with cost. The code for calculating the basic + * measures: BCWP, ACWP and BCWS is copied from + * {@link OrderPlanningModel}. At this moment this code cannot be reused + * as it's coupled with the logic for displaying the 'Earned Value' + * chart. We may consider to refactor this code in the future. + */ +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class CostStatusModel implements ICostStatusModel { + + @Autowired + private IOrderEarnedValueCalculator earnedValueCalculator; + + private Order order; + + public CostStatusModel() { + + } + + @Override + @Transactional(readOnly = true) + public BigDecimal getActualCostWorkPerformedAt(LocalDate date) { + return earnedValueCalculator.getActualCostWorkPerformedAt(order, date); + } + + @Override + public BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, + BigDecimal actualCost) { + return earnedValueCalculator.getCostPerformanceIndex(budgetedCost, + actualCost); + } + + @Override + public BigDecimal getCostVariance(BigDecimal budgetedCost, + BigDecimal actualCost) { + return earnedValueCalculator.getCostVariance(budgetedCost, actualCost); + } + + @Override + public BigDecimal getEstimateAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal costPerformanceIndex) { + return earnedValueCalculator.getEstimateAtCompletion( + budgetAtCompletion, costPerformanceIndex); + } + + @Override + public BigDecimal getVarianceAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal estimateAtCompletion) { + return budgetAtCompletion.subtract(estimateAtCompletion); + } + + @Override + public void setCurrentOrder(Order order) { + this.order = order; + } + + @Override + public BigDecimal getBudgetAtCompletion() { + return earnedValueCalculator.getBudgetAtCompletion(order); + } + + @Override + public BigDecimal getBudgetedCostWorkPerformedAt(LocalDate date) { + return earnedValueCalculator + .getBudgetedCostWorkPerformedAt(order, date); + } + +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java index 09a0e45e4..0702dbacc 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java @@ -21,29 +21,40 @@ package org.libreplan.web.dashboard; import static org.libreplan.web.I18nHelper._; -import java.awt.Color; -import java.awt.Font; import java.math.BigDecimal; -import java.util.Iterator; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.planner.entities.TaskStatusEnum; import org.libreplan.web.common.Util; +import org.libreplan.web.dashboard.DashboardModel.Interval; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import org.zkoss.zk.ui.util.Clients; import org.zkoss.zk.ui.util.GenericForwardComposer; -import org.zkoss.zul.CategoryModel; -import org.zkoss.zul.Chart; import org.zkoss.zul.Div; -import org.zkoss.zul.PieModel; -import org.zkoss.zul.SimpleCategoryModel; -import org.zkoss.zul.SimplePieModel; +import org.zkoss.zul.Grid; +import org.zkoss.zul.Label; import org.zkoss.zul.Window; +import br.com.digilabs.jqplot.Chart; +import br.com.digilabs.jqplot.JqPlotUtils; +import br.com.digilabs.jqplot.chart.BarChart; +import br.com.digilabs.jqplot.chart.PieChart; +import br.com.digilabs.jqplot.elements.Serie; + /** - * Controller for dashboardfororder view * @author Nacho Barrientos + * @author Diego Pino García + * + * Controller for dashboardfororder view */ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -53,12 +64,10 @@ public class DashboardController extends GenericForwardComposer { private Window dashboardWindow; - private Chart progressKPIglobalProgressChart; - private Chart progressKPItaskStatusChart; - private Chart progressKPItaskDeadlineViolationStatusChart; - private Chart timeKPImarginWithDeadlineChart; - private Chart timeKPIEstimationAccuracyChart; - private Chart timeKPILagInTaskCompletionChart; + private Grid gridTasksSummary; + private Grid gridMarginWithDeadline; + + private org.zkoss.zk.ui.Component costStatus; private Div projectDashboardChartsDiv; private Div projectDashboardNoTasksWarningDiv; @@ -69,171 +78,415 @@ public class DashboardController extends GenericForwardComposer { @Override public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { super.doAfterCompose(comp); - this.dashboardWindow = (Window)comp; + this.dashboardWindow = (Window) comp; + self.setAttribute("controller", this); Util.createBindingsFor(this.dashboardWindow); } public void setCurrentOrder(Order order) { dashboardModel.setCurrentOrder(order); - if(dashboardModel.tasksAvailable()) { - this.reloadCharts(); + if (dashboardModel.tasksAvailable()) { + showCharts(); } else { - this.hideChartsAndShowWarningMessage(); + hideCharts(); } if (this.dashboardWindow != null) { - Util.reloadBindings(this.dashboardWindow); + renderGlobalProgress(); + renderTaskStatus(); + renderTaskCompletationLag(); + renderTasksSummary(); + renderDeadlineViolation(); + renderMarginWithDeadline(); + renderEstimationAccuracy(); + renderCostStatus(order); } } - private void reloadCharts() { - generateProgressKPIglobalProgressChart(); - generateProgressKPItaskStatusChart(); - generateProgressKPItaskDeadlineViolationStatusChart(); - generateTimeKPImarginWithDeadlineChart(); - generateTimeKPIEstimationAccuracyChart(); - generateTimeKPILagInTaskCompletionChart(); + private void renderCostStatus(Order order) { + CostStatusController costStatusController = getCostStatusController(); + costStatusController.setOrder(order); + costStatusController.render(); } - private void hideChartsAndShowWarningMessage() { + private CostStatusController getCostStatusController() { + return (CostStatusController) costStatus.getAttribute("controller"); + } + + private void renderMarginWithDeadline() { + marginWithDeadline(dashboardModel.getMarginWithDeadLine()); + absoluteMarginWithDeadline(dashboardModel + .getAbsoluteMarginWithDeadLine()); + } + + private void marginWithDeadline(BigDecimal value) { + Label label = (Label) gridMarginWithDeadline + .getFellowIfAny("lblRelative"); + if (label != null) { + if (value != null) { + label.setValue(String.format(_("%.2f %%"), + value.doubleValue() * 100)); + } else { + label.setValue(_("")); + } + } + } + + private void absoluteMarginWithDeadline(Integer value) { + Label label = (Label) gridMarginWithDeadline + .getFellowIfAny("lblAbsolute"); + if (label != null) { + if (value != null) { + label.setValue(String.format(_("%d days"), value)); + } else { + label.setValue(_("")); + } + } + } + + private void renderDeadlineViolation() { + final String divId = "deadline-violation"; + + PieChart pieChart = new PieChart( + _("Deadline Violation")); + pieChart.addValue(_("On schedule"), + dashboardModel.getPercentageOfOnScheduleTasks()); + pieChart.addValue(_("Violated deadline"), + dashboardModel.getPercentageOfTasksWithViolatedDeadline()); + pieChart.addValue(_("No deadline"), + dashboardModel.getPercentageOfTasksWithNoDeadline()); + renderChart(pieChart, divId); + } + + private void renderChart(Chart chart, String divId) { + String jsCode = JqPlotUtils.createJquery(chart, divId); + Clients.evalJavaScript(jsCode); + } + + private void renderTaskCompletationLag() { + final String divId = "task-completation-lag"; + + BarChart barChart; + barChart = new BarChart("Task Completation Lead/Lag"); + + barChart.setFillZero(true); + barChart.setHighlightMouseDown(true); + barChart.setStackSeries(false); + barChart.setBarMargin(30); + + barChart.addSeries(new Serie("Tasks")); + + TaskCompletationData taskCompletationData = TaskCompletationData + .create(dashboardModel); + barChart.setTicks(taskCompletationData.getTicks()); + barChart.addValues(taskCompletationData.getValues()); + + barChart.getAxes().getXaxis() + .setLabel(_("Number of Days / Days Interval")); + + renderChart(barChart, divId); + } + + private void renderEstimationAccuracy() { + final String divId = "estimation-accuracy"; + + BarChart barChart; + barChart = new BarChart("Estimation Accuracy"); + + barChart.setFillZero(true); + barChart.setHighlightMouseDown(true); + barChart.setStackSeries(false); + barChart.setBarMargin(30); + + barChart.addSeries(new Serie("Tasks")); + + EstimationAccuracy estimationAccuracyData = EstimationAccuracy + .create(dashboardModel); + barChart.setTicks(estimationAccuracyData.getTicks()); + barChart.addValues(estimationAccuracyData.getValues()); + + barChart.getAxes().getXaxis() + .setLabel(_("Number of Tasks / % Deviation")); + + renderChart(barChart, divId); + } + + private void renderTasksSummary() { + Map taskStatus = dashboardModel + .calculateTaskStatus(); + + taskStatus("lblTasksFinished", taskStatus.get(TaskStatusEnum.FINISHED)); + taskStatus("lblTasksBlocked", taskStatus.get(TaskStatusEnum.BLOCKED)); + taskStatus("lblTasksInProgress", + taskStatus.get(TaskStatusEnum.IN_PROGRESS)); + taskStatus("lblTasksReadyToStart", + taskStatus.get(TaskStatusEnum.READY_TO_START)); + } + + private void taskStatus(String key, Integer value) { + Label label = (Label) gridTasksSummary.getFellowIfAny(key); + if (label != null) { + label.setValue(String.format(_("%d tasks"), value)); + } + } + + private void renderTaskStatus() { + final String divId = "task-status"; + + PieChart taskStatus = new PieChart(_("Task Status")); + taskStatus.addValue(_("Finished"), + dashboardModel.getPercentageOfFinishedTasks()); + taskStatus.addValue(_("In progress"), + dashboardModel.getPercentageOfInProgressTasks()); + taskStatus.addValue(_("Ready to start"), + dashboardModel.getPercentageOfReadyToStartTasks()); + taskStatus.addValue(_("Blocked"), + dashboardModel.getPercentageOfBlockedTasks()); + renderChart(taskStatus, divId); + } + + private void renderGlobalProgress() { + GlobalProgress globalProgress = GlobalProgress.create(); + + // Current values + globalProgress.current(GlobalProgress.CRITICAL_PATH_DURATION, + dashboardModel.getCriticalPathProgressByDuration()); + globalProgress.current(GlobalProgress.CRITICAL_PATH_HOURS, + dashboardModel.getCriticalPathProgressByNumHours()); + globalProgress.current(GlobalProgress.ALL_TASKS_HOURS, + dashboardModel.getAdvancePercentageByHours()); + // Expected values + globalProgress.expected(GlobalProgress.CRITICAL_PATH_DURATION, + dashboardModel.getExpectedCriticalPathProgressByDuration()); + globalProgress.expected(GlobalProgress.CRITICAL_PATH_HOURS, + dashboardModel.getExpectedCriticalPathProgressByNumHours()); + globalProgress.expected(GlobalProgress.ALL_TASKS_HOURS, + dashboardModel.getExpectedAdvancePercentageByHours()); + + globalProgress.render(); + } + + private void showCharts() { + projectDashboardChartsDiv.setVisible(true); + projectDashboardNoTasksWarningDiv.setVisible(false); + } + + private void hideCharts() { projectDashboardChartsDiv.setVisible(false); projectDashboardNoTasksWarningDiv.setVisible(true); } - private void generateTimeKPILagInTaskCompletionChart() { - CategoryModel categoryModel; - categoryModel = refreshTimeKPILagInTaskCompletionCategoryModel(); - Font labelFont = new Font("serif", Font.PLAIN, 10); - timeKPILagInTaskCompletionChart.setXAxisTickFont(labelFont); - Color[] seriesColorMappings = {Color.BLUE}; - timeKPILagInTaskCompletionChart.setAttribute("series-color-mappings", - seriesColorMappings); - timeKPILagInTaskCompletionChart.setModel(categoryModel); - } + /** + * + * @author Diego Pino García + * + */ + static class GlobalProgress { - private void generateTimeKPIEstimationAccuracyChart() { - CategoryModel categoryModel; - categoryModel = refreshTimeKPIEstimationAccuracyCategoryModel(); - Font labelFont = new Font("serif", Font.PLAIN, 10); - timeKPIEstimationAccuracyChart.setXAxisTickFont(labelFont); - Color[] seriesColorMappings = {Color.BLUE}; - timeKPIEstimationAccuracyChart.setAttribute("series-color-mappings", - seriesColorMappings); - timeKPIEstimationAccuracyChart.setModel(categoryModel); - } + public static final String ALL_TASKS_HOURS = _("All tasks (hours)"); + + public static final String CRITICAL_PATH_HOURS = _("Critical path (hours)"); + + public static final String CRITICAL_PATH_DURATION = _("Critical path (duration)"); + + private final Map current = new LinkedHashMap(); + + private final Map expected = new LinkedHashMap(); + + private static List series = new ArrayList() { + { + add(Series.create(_("Current"), "#33c")); + add(Series.create(_("Expected"), "#c33")); + } + }; + + private GlobalProgress() { - private void generateTimeKPImarginWithDeadlineChart() { - CategoryModel categoryModel; - categoryModel = refreshTimeKPImarginWithDeadlineCategoryModel(); - if (categoryModel == null) { // Project has no deadline set. - timeKPImarginWithDeadlineChart.setVisible(false); - return; } - timeKPImarginWithDeadlineChart.setAttribute("range-axis-lower-bound", - new Double(-3.0)); - timeKPImarginWithDeadlineChart.setAttribute("range-axis-upper-bound", - new Double(3.0)); - Color[] seriesColorMappings = new Color[1]; - if(dashboardModel.getMarginWithDeadLine().compareTo(BigDecimal.ZERO) >= 0) { - seriesColorMappings[0] = Color.GREEN; - } else { - seriesColorMappings[0] = Color.RED; + + public void current(String key, BigDecimal value) { + current.put(key, value); } - timeKPImarginWithDeadlineChart.setAttribute("series-color-mappings", - seriesColorMappings); - timeKPImarginWithDeadlineChart.setModel(categoryModel); - } - private void generateProgressKPItaskStatusChart() { - PieModel model = refreshProgressKPItaskStatusPieModel(); - progressKPItaskStatusChart.setModel(model); - } - - private void generateProgressKPItaskDeadlineViolationStatusChart() { - PieModel model = refreshProgressKPItaskDeadlieViolationStatusPieModel(); - progressKPItaskDeadlineViolationStatusChart.setModel(model); - } - - private void generateProgressKPIglobalProgressChart() { - CategoryModel categoryModel; - categoryModel = refreshProgressKPIglobalProgressCategoryModel(); - progressKPIglobalProgressChart.setAttribute("range-axis-lower-bound", - new Double(0.0)); - progressKPIglobalProgressChart.setAttribute("range-axis-upper-bound", - new Double(100.0)); - progressKPIglobalProgressChart.setModel(categoryModel); - } - - private PieModel refreshProgressKPItaskStatusPieModel() { - PieModel model = new SimplePieModel(); - model.setValue(_("Finished"), dashboardModel.getPercentageOfFinishedTasks()); - model.setValue(_("In progress"), dashboardModel.getPercentageOfInProgressTasks()); - model.setValue(_("Ready to start"), dashboardModel.getPercentageOfReadyToStartTasks()); - model.setValue(_("Blocked"), dashboardModel.getPercentageOfBlockedTasks()); - return model; - } - - private PieModel refreshProgressKPItaskDeadlieViolationStatusPieModel() { - PieModel model = new SimplePieModel(); - model.setValue(_("On schedule"), dashboardModel.getPercentageOfOnScheduleTasks()); - model.setValue(_("Violated deadline"), dashboardModel.getPercentageOfTasksWithViolatedDeadline()); - model.setValue(_("No deadline"), dashboardModel.getPercentageOfTasksWithNoDeadline()); - return model; - } - - private CategoryModel refreshProgressKPIglobalProgressCategoryModel() { - CategoryModel result = new SimpleCategoryModel(); - result.setValue(_("Current"), _("All tasks (hours)"), - dashboardModel.getAdvancePercentageByHours()); - result.setValue(_("Expected"), _("All tasks (hours)"), - dashboardModel.getTheoreticalAdvancePercentageByHoursUntilNow()); - result.setValue(_("Current"), _("Critical path (hours)"), - dashboardModel.getCriticalPathProgressByNumHours()); - result.setValue(_("Expected"), _("Critical path (hours)"), dashboardModel - .getTheoreticalProgressByNumHoursForCriticalPathUntilNow()); - result.setValue(_("Current"), _("Critical path (duration)"), - dashboardModel.getCriticalPathProgressByDuration()); - result.setValue(_("Expected"), _("Critical path (duration)"), - dashboardModel.getTheoreticalProgressByDurationForCriticalPathUntilNow()); - return result; - } - - private CategoryModel refreshTimeKPImarginWithDeadlineCategoryModel() { - CategoryModel result = null; - BigDecimal marginWithDeadLine = dashboardModel.getMarginWithDeadLine(); - if (marginWithDeadLine != null) { - result = new SimpleCategoryModel(); - result.setValue(_("None"), _("Deviation"), marginWithDeadLine); + public void expected(String key, BigDecimal value) { + expected.put(key, value); } - return result; + + public static GlobalProgress create() { + return new GlobalProgress(); + } + + public String getPercentages() { + return String.format("'[%s, %s]'", + jsonifyPercentages(current.values()), + jsonifyPercentages(expected.values())); + } + + private String jsonifyPercentages(Collection array) { + List result = new ArrayList(); + + int i = 1; + for (BigDecimal each : array) { + result.add(String.format("[%.2f, %d]", each.doubleValue(), i++)); + } + return String.format("[%s]", StringUtils.join(result, ",")); + } + + private String jsonify(Collection list) { + Collection result = new ArrayList(); + for (Object each : list) { + if (each.getClass() == String.class) { + result.add(String.format("\"%s\"", each.toString())); + } else { + result.add(String.format("%s", each.toString())); + } + } + return String.format("'[%s]'", StringUtils.join(result, ',')); + } + + public String getSeries() { + return jsonify(series); + } + + /** + * The order of the ticks is taken from the keys in current + * + * @return + */ + public String getTicks() { + return jsonify(current.keySet()); + } + + public void render() { + String command = String.format( + "global_progress.render(%s, %s, %s);", getPercentages(), + getTicks(), getSeries()); + Clients.evalJavaScript(command); + } + } - private CategoryModel refreshTimeKPIEstimationAccuracyCategoryModel() { - CategoryModel result = new SimpleCategoryModel(); - List values = dashboardModel.getFinishedTasksEstimationAccuracyHistogram(); - Iterator it = values.iterator(); - for(int ii= DashboardModel.EA_STRETCHES_MIN_VALUE; - ii < DashboardModel.EA_STRETCHES_MAX_VALUE; - ii += DashboardModel.EA_STRETCHES_PERCENTAGE_STEP) { - result.setValue(_("None"), _(String.valueOf(ii)), it.next()); + /** + * + * @author Diego Pino García + * + */ + static class Series { + + private String label; + + private String color; + + private Series() { + } - result.setValue(_("None"), - _(">"+DashboardModel.EA_STRETCHES_MAX_VALUE), - it.next()); - return result; + + public static Series create(String label) { + Series series = new Series(); + series.label = label; + return series; + } + + public static Series create(String label, String color) { + Series series = new Series(); + series.label = label; + series.color = color; + return series; + } + + @Override + public String toString() { + return String.format("{\"label\": \"%s\", \"color\": \"%s\"}", + label, color); + } + } - private CategoryModel refreshTimeKPILagInTaskCompletionCategoryModel() { - CategoryModel result = new SimpleCategoryModel(); - List values = dashboardModel.getLagInTaskCompletionHistogram(); - Iterator it = values.iterator(); - for(double ii= DashboardModel.LTC_STRETCHES_MIN_VALUE; - ii < DashboardModel.LTC_STRETCHES_MAX_VALUE; - ii += DashboardModel.LTC_STRETCHES_STEP) { - result.setValue(_("None"), _(String.valueOf(ii)), it.next()); + /** + * + * @author Diego Pino García + * + */ + static class TaskCompletationData { + + private final IDashboardModel dashboardModel; + + private Map taskCompletationData; + + private TaskCompletationData(IDashboardModel dashboardModel) { + this.dashboardModel = dashboardModel; } - result.setValue(_("None"), - _(">"+DashboardModel.LTC_STRETCHES_MAX_VALUE), - it.next()); - return result; + + public static TaskCompletationData create(IDashboardModel dashboardModel) { + return new TaskCompletationData(dashboardModel); + } + + private Map getData() { + if (taskCompletationData == null) { + taskCompletationData = dashboardModel + .calculateTaskCompletation(); + } + return taskCompletationData; + } + + public String[] getTicks() { + Set intervals = getData().keySet(); + String[] result = new String[intervals.size()]; + int i = 0; + for (Interval each : intervals) { + result[i++] = each.toString(); + + } + return result; + } + + public Collection getValues() { + return getData().values(); + } + } + + /** + * + * @author Diego Pino García + * + */ + static class EstimationAccuracy { + + private final IDashboardModel dashboardModel; + + private Map estimationAccuracyData; + + private EstimationAccuracy(IDashboardModel dashboardModel) { + this.dashboardModel = dashboardModel; + } + + public static EstimationAccuracy create(IDashboardModel dashboardModel) { + return new EstimationAccuracy(dashboardModel); + } + + private Map getData() { + if (estimationAccuracyData == null) { + estimationAccuracyData = dashboardModel + .calculateEstimationAccuracy(); + } + return estimationAccuracyData; + } + + public String[] getTicks() { + Set intervals = getData().keySet(); + String[] result = new String[intervals.size()]; + int i = 0; + for (Interval each : intervals) { + result[i++] = each.toString(); + + } + return result; + } + + public Collection getValues() { + return getData().values(); + } + + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java index 2e53c3f5a..14f69d234 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java @@ -22,10 +22,14 @@ package org.libreplan.web.dashboard; import java.math.BigDecimal; import java.math.MathContext; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.EnumMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.joda.time.Days; import org.joda.time.LocalDate; @@ -64,14 +68,14 @@ public class DashboardModel implements IDashboardModel { public static double LTC_STRETCHES_MIN_VALUE = 0; public static double LTC_STRETCHES_MAX_VALUE = 0; - private Order currentOrder; private Integer taskCount = null; - private Map taskStatusStats; - private Map taskDeadlineViolationStatusStats; + private final Map taskStatusStats; + private final Map taskDeadlineViolationStatusStats; private List taskEstimationAccuracyHistogram; private BigDecimal marginWithDeadLine; + private Integer absoluteMarginWithDeadLine; private List lagInTaskCompletionHistogram; public DashboardModel() { @@ -81,10 +85,11 @@ public class DashboardModel implements IDashboardModel { TaskDeadlineViolationStatusEnum.class); } + @Override public void setCurrentOrder(Order order) { this.currentOrder = order; this.taskCount = null; - if(tasksAvailable()) { + if (tasksAvailable()) { this.calculateTaskStatusStatistics(); this.calculateTaskViolationStatusStatistics(); this.calculateMarginWithDeadLine(); @@ -94,91 +99,110 @@ public class DashboardModel implements IDashboardModel { } /* Progress KPI: "Number of tasks by status" */ + @Override public BigDecimal getPercentageOfFinishedTasks() { return taskStatusStats.get(TaskStatusEnum.FINISHED); } + @Override public BigDecimal getPercentageOfInProgressTasks() { return taskStatusStats.get(TaskStatusEnum.IN_PROGRESS); } + @Override public BigDecimal getPercentageOfReadyToStartTasks() { return taskStatusStats.get(TaskStatusEnum.READY_TO_START); } + @Override public BigDecimal getPercentageOfBlockedTasks() { return taskStatusStats.get(TaskStatusEnum.BLOCKED); } /* Progress KPI: "Deadline violation" */ + @Override public BigDecimal getPercentageOfOnScheduleTasks() { - return taskDeadlineViolationStatusStats.get(TaskDeadlineViolationStatusEnum.ON_SCHEDULE); + return taskDeadlineViolationStatusStats + .get(TaskDeadlineViolationStatusEnum.ON_SCHEDULE); } + @Override public BigDecimal getPercentageOfTasksWithViolatedDeadline() { - return taskDeadlineViolationStatusStats.get(TaskDeadlineViolationStatusEnum.DEADLINE_VIOLATED); + return taskDeadlineViolationStatusStats + .get(TaskDeadlineViolationStatusEnum.DEADLINE_VIOLATED); } + @Override public BigDecimal getPercentageOfTasksWithNoDeadline() { - return taskDeadlineViolationStatusStats.get(TaskDeadlineViolationStatusEnum.NO_DEADLINE); + return taskDeadlineViolationStatusStats + .get(TaskDeadlineViolationStatusEnum.NO_DEADLINE); } /* Progress KPI: "Global Progress of the Project" */ - public BigDecimal getAdvancePercentageByHours(){ - TaskGroup rootAsTaskGroup = (TaskGroup)getRootTask(); - if (this.getRootTask() == null) { + @Override + public BigDecimal getAdvancePercentageByHours() { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getProgressAllByNumHours(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask.getProgressAllByNumHours()); } - public BigDecimal getTheoreticalAdvancePercentageByHoursUntilNow(){ - TaskGroup rootAsTaskGroup = (TaskGroup)getRootTask(); - if (this.getRootTask() == null) { + private BigDecimal asPercentage(BigDecimal value) { + return value != null ? value.multiply(BigDecimal.valueOf(100)) + : BigDecimal.ZERO; + } + + @Override + public BigDecimal getExpectedAdvancePercentageByHours() { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getTheoreticalProgressByNumHoursForAllTasksUntilNow(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask + .getTheoreticalProgressByNumHoursForAllTasksUntilNow()); } + @Override public BigDecimal getCriticalPathProgressByNumHours() { - TaskGroup rootAsTaskGroup = (TaskGroup)getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getCriticalPathProgressByNumHours(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask.getCriticalPathProgressByNumHours()); } - public BigDecimal getTheoreticalProgressByNumHoursForCriticalPathUntilNow() { - TaskGroup rootAsTaskGroup = (TaskGroup)getRootTask(); - if (this.getRootTask() == null) { + @Override + public BigDecimal getExpectedCriticalPathProgressByNumHours() { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getTheoreticalProgressByNumHoursForCriticalPathUntilNow(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask + .getTheoreticalProgressByNumHoursForCriticalPathUntilNow()); } + @Override public BigDecimal getCriticalPathProgressByDuration() { - TaskGroup rootAsTaskGroup = (TaskGroup)getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getCriticalPathProgressByDuration(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask.getCriticalPathProgressByDuration()); } - public BigDecimal getTheoreticalProgressByDurationForCriticalPathUntilNow() { - TaskGroup rootAsTaskGroup = (TaskGroup)getRootTask(); - if (this.getRootTask() == null) { + @Override + public BigDecimal getExpectedCriticalPathProgressByDuration() { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getTheoreticalProgressByDurationForCriticalPathUntilNow(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask + .getTheoreticalProgressByDurationForCriticalPathUntilNow()); } /* Time KPI: Margin with deadline */ + @Override public BigDecimal getMarginWithDeadLine() { return this.marginWithDeadLine; } @@ -207,7 +231,35 @@ public class DashboardModel implements IDashboardModel { BigDecimal.ROUND_HALF_EVEN); } + @Override + public Integer getAbsoluteMarginWithDeadLine() { + if (absoluteMarginWithDeadLine == null) { + calculateAbsoluteMarginWithDeadLine(); + } + return absoluteMarginWithDeadLine; + } + + private void calculateAbsoluteMarginWithDeadLine() { + TaskElement rootTask = getRootTask(); + Date deadline = currentOrder.getDeadline(); + + if (rootTask == null) { + throw new RuntimeException("Root task is null"); + } + if (deadline == null) { + this.absoluteMarginWithDeadLine = null; + return; + } + absoluteMarginWithDeadLine = daysBetween(rootTask.getEndAsLocalDate(), + LocalDate.fromDateFields(deadline)); + } + + private int daysBetween(LocalDate start, LocalDate end) { + return Days.daysBetween(start, end).getDays(); + } + /* Time KPI: Estimation accuracy */ + @Override public List getFinishedTasksEstimationAccuracyHistogram() { return this.taskEstimationAccuracyHistogram; } @@ -216,21 +268,19 @@ public class DashboardModel implements IDashboardModel { if (this.getRootTask() == null) { throw new RuntimeException("Root task is null"); } - CalculateFinishedTasksEstimationDeviationVisitor visitor = - new CalculateFinishedTasksEstimationDeviationVisitor(); + CalculateFinishedTasksEstimationDeviationVisitor visitor = new CalculateFinishedTasksEstimationDeviationVisitor(); TaskElement rootTask = getRootTask(); rootTask.acceptVisitor(visitor); List deviations = visitor.getDeviations(); // [-100, -90), [-90, -80), ..., [190, 200), [200, inf) this.taskEstimationAccuracyHistogram = createHistogram( - EA_STRETCHES_MIN_VALUE, - EA_STRETCHES_MAX_VALUE, - EA_STRETCHES_PERCENTAGE_STEP, - deviations); + EA_STRETCHES_MIN_VALUE, EA_STRETCHES_MAX_VALUE, + EA_STRETCHES_PERCENTAGE_STEP, deviations); } /* Time KPI: Lead/Lag in task completion */ + @Override public List getLagInTaskCompletionHistogram() { return this.lagInTaskCompletionHistogram; } @@ -239,8 +289,7 @@ public class DashboardModel implements IDashboardModel { if (this.getRootTask() == null) { throw new RuntimeException("Root task is null"); } - CalculateFinishedTasksLagInCompletionVisitor visitor = - new CalculateFinishedTasksLagInCompletionVisitor(); + CalculateFinishedTasksLagInCompletionVisitor visitor = new CalculateFinishedTasksLagInCompletionVisitor(); TaskElement rootTask = getRootTask(); rootTask.acceptVisitor(visitor); List deviations = visitor.getDeviations(); @@ -253,45 +302,228 @@ public class DashboardModel implements IDashboardModel { LTC_STRETCHES_MAX_VALUE = Collections.max(deviations); } LTC_STRETCHES_STEP = (LTC_STRETCHES_MAX_VALUE - LTC_STRETCHES_MIN_VALUE) - /LTC_NUMBER_OF_INTERVALS; + / LTC_NUMBER_OF_INTERVALS; this.lagInTaskCompletionHistogram = createHistogram( - LTC_STRETCHES_MIN_VALUE, - LTC_STRETCHES_MAX_VALUE, - LTC_STRETCHES_STEP, - deviations); + LTC_STRETCHES_MIN_VALUE, LTC_STRETCHES_MAX_VALUE, + LTC_STRETCHES_STEP, deviations); } private List createHistogram(double lowBound, double highBound, double intervalStep, List values) { double variableRange = highBound - lowBound; /* TODO: What if highBound == lowBound? */ - int numberOfClasses = (int)(variableRange/intervalStep); - int[] classes = new int[numberOfClasses+1]; + int numberOfClasses = (int) (variableRange / intervalStep); + int[] classes = new int[numberOfClasses + 1]; - for(Double value: values) { + for (Double value : values) { int index; if (value >= highBound) { index = numberOfClasses; } else { - index = (int)(numberOfClasses * - (((value.doubleValue() - lowBound))/variableRange)); + index = (int) (numberOfClasses * (((value.doubleValue() - lowBound)) / variableRange)); } classes[index]++; } List histogram = new ArrayList(); int numberOfConsideredTasks = values.size(); - for (int numberOfElementsInClass: classes) { + for (int numberOfElementsInClass : classes) { Double relativeCount = new Double(0.0); if (numberOfConsideredTasks > 0) { - relativeCount = new Double(1.0*numberOfElementsInClass/ - numberOfConsideredTasks); + relativeCount = new Double(1.0 * numberOfElementsInClass + / numberOfConsideredTasks); } histogram.add(relativeCount); } return histogram; } + /** + * Calculates the task completation deviations for the current order + * + * All the deviations are groups in Interval.MAX_INTERVALS intervals of + * equal size. If the order contains just one single task then, the upper + * limit will be the deviation of the task + 3, and the lower limit will be + * deviation of the task - 2 + * + * Each {@link Interval} contains the number of tasks that fit in that + * interval + * + * @return + */ + @Override + public Map calculateTaskCompletation() { + Map result = new LinkedHashMap(); + Double max, min; + + // Get deviations of finished tasks, calculate max, min and delta + List deviations = getTaskLagDeviations(); + if (deviations.isEmpty()) { + return result; + } + if (deviations.size() == 1) { + max = deviations.get(0).doubleValue() + 3; + min = deviations.get(0).doubleValue() - 2; + } else { + max = Collections.max(deviations); + min = Collections.min(deviations); + } + double delta = (max - min) / Interval.MAX_INTERVALS; + + // Create MAX_INTERVALS + double from = min; + for (int i = 0; i < Interval.MAX_INTERVALS; i++) { + result.put(Interval.create(from, from + delta), Integer.valueOf(0)); + from = from + delta; + } + + // Construct map with number of tasks for each interval + final Set intervals = result.keySet(); + for (Double each : deviations) { + Interval interval = Interval.containingValue(intervals, each); + if (interval != null) { + Integer value = result.get(interval); + result.put(interval, value + 1); + } + } + return result; + } + + private List getTaskLagDeviations() { + if (this.getRootTask() == null) { + throw new RuntimeException("Root task is null"); + } + CalculateFinishedTasksLagInCompletionVisitor visitor = new CalculateFinishedTasksLagInCompletionVisitor(); + TaskElement rootTask = getRootTask(); + rootTask.acceptVisitor(visitor); + return visitor.getDeviations(); + } + + /** + * Calculates the estimation accuracy deviations for the current order + * + * All the deviations are groups in Interval.MAX_INTERVALS intervals of + * equal size. If the order contains just one single task then, the upper + * limit will be the deviation of the task + 30, and the lower limit will be + * deviation of the task - 20 + * + * Each {@link Interval} contains the number of tasks that fit in that + * interval + * + * @return + */ + @Override + public Map calculateEstimationAccuracy() { + Map result = new LinkedHashMap(); + Double max, min; + + // Get deviations of finished tasks, calculate max, min and delta + List deviations = getEstimationAccuracyDeviations(); + if (deviations.isEmpty()) { + return result; + } + if (deviations.size() == 1) { + max = deviations.get(0).doubleValue() + 30; + min = deviations.get(0).doubleValue() - 20; + } else { + max = Collections.max(deviations); + min = Collections.min(deviations); + } + double delta = (max - min) / Interval.MAX_INTERVALS; + + // Create MAX_INTERVALS + double from = min; + for (int i = 0; i < Interval.MAX_INTERVALS; i++) { + result.put(Interval.create(from, from + delta), Integer.valueOf(0)); + from = from + delta; + } + + // Construct map with number of tasks for each interval + final Set intervals = result.keySet(); + for (Double each : deviations) { + Interval interval = Interval.containingValue(intervals, each); + if (interval != null) { + Integer value = result.get(interval); + result.put(interval, value + 1); + } + } + return result; + } + + private List getEstimationAccuracyDeviations() { + if (this.getRootTask() == null) { + throw new RuntimeException("Root task is null"); + } + CalculateFinishedTasksEstimationDeviationVisitor visitor = new CalculateFinishedTasksEstimationDeviationVisitor(); + TaskElement rootTask = getRootTask(); + rootTask.acceptVisitor(visitor); + return visitor.getDeviations(); + } + + /** + * + * @author Diego Pino García + * + */ + static class Interval { + + public static final double MAX_INTERVALS = 6; + + private double min; + + private double max; + + private Interval() { + + } + + public static Interval create(double min, double max) { + return new Interval(min, max); + } + + private Interval(double min, double max) { + this.min = min; + this.max = max; + } + + public static Interval copy(Interval interval) { + return new Interval(interval.min, interval.max); + } + + public static Interval containingValue(Collection intervals, + Double value) { + for (Interval each : intervals) { + if (each.includes(value)) { + return each; + } + } + return null; + } + + private boolean includes(double value) { + return (value >= min) && (value <= max); + } + + @Override + public String toString() { + return String.format("[%d, %d]", (int) Math.ceil(min), + (int) Math.ceil(max)); + } + + } + + @Override + public Map calculateTaskStatus() { + AccumulateTasksStatusVisitor visitor = new AccumulateTasksStatusVisitor(); + TaskElement rootTask = getRootTask(); + if (this.getRootTask() == null) { + throw new RuntimeException("Root task is null"); + } + resetTasksStatusInGraph(); + rootTask.acceptVisitor(visitor); + return visitor.getTaskStatusData(); + } + private void calculateTaskStatusStatistics() { AccumulateTasksStatusVisitor visitor = new AccumulateTasksStatusVisitor(); TaskElement rootTask = getRootTask(); @@ -311,7 +543,8 @@ public class DashboardModel implements IDashboardModel { throw new RuntimeException("Root task is null"); } rootTask.acceptVisitor(visitor); - Map count = visitor.getTaskDeadlineViolationStatusData(); + Map count = visitor + .getTaskDeadlineViolationStatusData(); mapAbsoluteValuesToPercentages(count, taskDeadlineViolationStatusStats); } @@ -342,8 +575,9 @@ public class DashboardModel implements IDashboardModel { } private int countTasksInAResultMap(Map map) { - /* It's only needed to count the number of tasks once - * each time setOrder is called. + /* + * It's only needed to count the number of tasks once each time setOrder + * is called. */ if (this.taskCount != null) { return this.taskCount.intValue(); @@ -356,6 +590,7 @@ public class DashboardModel implements IDashboardModel { return sum; } + @Override public boolean tasksAvailable() { return getRootTask() != null; } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/ICostStatusModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/ICostStatusModel.java new file mode 100644 index 000000000..a150ab2da --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/ICostStatusModel.java @@ -0,0 +1,60 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2010-2012 Igalia, S.L. + * + * 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 Affero 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 . + */ + +package org.libreplan.web.dashboard; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.libreplan.business.orders.entities.Order; + +/** + * + * @author Diego Pino García + * + */ +interface ICostStatusModel { + + // Actual Cost Work Performed (ACWP) + BigDecimal getActualCostWorkPerformedAt(LocalDate date); + + // Budget At Completion (BAC) + BigDecimal getBudgetAtCompletion(); + + // Budgeted Cost Work Performed (BCWP) + BigDecimal getBudgetedCostWorkPerformedAt(LocalDate date); + + // Cost Performance Index (CPI) + BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, + BigDecimal actualCost); + + // Cost Variance (CV) + BigDecimal getCostVariance(BigDecimal budgetedCost, BigDecimal actualCost); + + // Estimate at Completion (EAC) + BigDecimal getEstimateAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal costPerformanceIndex); + + // Variance at Completion (VAC) + BigDecimal getVarianceAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal estimateAtCompletion); + + void setCurrentOrder(Order order); + +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java index ce86cff33..c8241560f 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java @@ -21,8 +21,11 @@ package org.libreplan.web.dashboard; import java.math.BigDecimal; import java.util.List; +import java.util.Map; import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.planner.entities.TaskStatusEnum; +import org.libreplan.web.dashboard.DashboardModel.Interval; interface IDashboardModel { @@ -49,22 +52,31 @@ interface IDashboardModel { /* Progress KPI: "Global Progress of the Project" */ BigDecimal getAdvancePercentageByHours(); - BigDecimal getTheoreticalAdvancePercentageByHoursUntilNow(); + BigDecimal getExpectedAdvancePercentageByHours(); BigDecimal getCriticalPathProgressByNumHours(); - BigDecimal getTheoreticalProgressByNumHoursForCriticalPathUntilNow(); + BigDecimal getExpectedCriticalPathProgressByNumHours(); BigDecimal getCriticalPathProgressByDuration(); - BigDecimal getTheoreticalProgressByDurationForCriticalPathUntilNow(); + BigDecimal getExpectedCriticalPathProgressByDuration(); /* Time KPI: "Margin with deadline" */ BigDecimal getMarginWithDeadLine(); + Integer getAbsoluteMarginWithDeadLine(); + /* Time KPI: "Estimation accuracy" */ List getFinishedTasksEstimationAccuracyHistogram(); /* Time KPI: "Lead/Lag in task completion" */ List getLagInTaskCompletionHistogram(); + + Map calculateTaskStatus(); + + Map calculateTaskCompletation(); + + Map calculateEstimationAccuracy(); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/materials/MaterialsController.java b/libreplan-webapp/src/main/java/org/libreplan/web/materials/MaterialsController.java index e002762f3..268b24ceb 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/materials/MaterialsController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/materials/MaterialsController.java @@ -554,4 +554,9 @@ public class MaterialsController extends } } } + + public String getMoneyFormat() { + return Util.getMoneyFormat(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/AssignedHoursToOrderElementController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/AssignedHoursToOrderElementController.java index 69304faf0..5e2a0edcd 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/AssignedHoursToOrderElementController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/AssignedHoursToOrderElementController.java @@ -159,4 +159,8 @@ public class AssignedHoursToOrderElementController extends } } + public String getCurrencySymbol() { + return Util.getCurrencySymbol(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/DetailsOrderElementController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/DetailsOrderElementController.java index 6694ce283..27005eaaf 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/DetailsOrderElementController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/DetailsOrderElementController.java @@ -67,4 +67,8 @@ public class DetailsOrderElementController extends return !orderElementModel.getOrderElement().isLeaf(); } + public String getMoneyFormat() { + return Util.getMoneyFormat(); + } + } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java index c1d535419..3411c378f 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -33,6 +33,7 @@ import org.libreplan.business.labels.entities.Label; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderLineGroup; +import org.libreplan.business.planner.entities.PositionConstraintType; import org.libreplan.business.qualityforms.entities.QualityForm; import org.libreplan.business.resources.entities.Criterion; import org.libreplan.business.resources.entities.CriterionType; @@ -48,6 +49,7 @@ import org.zkoss.zk.ui.Desktop; * * @author Óscar González Fernández * @author Diego Pino García + * @author Manuel Rego Casasnovas */ public interface IOrderModel extends IIntegrationEntityModel { @@ -139,4 +141,6 @@ public interface IOrderModel extends IIntegrationEntityModel { boolean alreadyExistsRepeatedEndDate(Date value); + boolean isAnyTaskWithConstraint(PositionConstraintType type); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java index 6578824fa..bac6c54ad 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -51,6 +51,7 @@ import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.Order.SchedulingMode; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderStatusEnum; +import org.libreplan.business.planner.entities.PositionConstraintType; import org.libreplan.business.templates.entities.OrderTemplate; import org.libreplan.business.users.entities.UserRole; import org.libreplan.web.common.IMessagesForUser; @@ -93,6 +94,7 @@ import org.zkoss.zul.Comboitem; import org.zkoss.zul.ComboitemRenderer; import org.zkoss.zul.Constraint; import org.zkoss.zul.Datebox; +import org.zkoss.zul.Decimalbox; import org.zkoss.zul.Grid; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; @@ -116,6 +118,7 @@ import org.zkoss.zul.api.Window; * * @author Óscar González Fernández * @author Lorenzo Tilve Álvaro + * @author Manuel Rego Casasnovas */ @org.springframework.stereotype.Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -398,11 +401,45 @@ public class OrderCRUDController extends GenericForwardComposer { return result; } - private void setConstraintsFor(SchedulingMode mode) { - initDate.setConstraint(mode == SchedulingMode.FORWARD ? "no empty" - : null); - deadline.setConstraint(mode == SchedulingMode.BACKWARDS ? "no empty" - : null); + private void setConstraintsFor(final SchedulingMode mode) { + initDate.setConstraint(new Constraint() { + + @Override + public void validate(Component comp, Object value) + throws WrongValueException { + if (value == null) { + if (mode == SchedulingMode.FORWARD) { + throw new WrongValueException( + comp, + _("Starting date cannot be empty in forward mode")); + } + if (orderModel + .isAnyTaskWithConstraint(PositionConstraintType.AS_SOON_AS_POSSIBLE)) { + throw new WrongValueException(comp, + _("Starting date cannot be empty because there is a task with constraint \"as soon as possible\"")); + } + } + } + }); + deadline.setConstraint(new Constraint() { + + @Override + public void validate(Component comp, Object value) + throws WrongValueException { + if (value == null) { + if (mode == SchedulingMode.BACKWARDS) { + throw new WrongValueException( + comp, + _("Deadline cannot be empty in backwards mode")); + } + if (orderModel + .isAnyTaskWithConstraint(PositionConstraintType.AS_LATE_AS_POSSIBLE)) { + throw new WrongValueException(comp, + _("Deadline cannot be empty because there is a task with constraint \"as late as possible\"")); + } + } + } + }); } private void changeFocusAccordingTo(SchedulingMode chosen) { @@ -1187,7 +1224,7 @@ public class OrderCRUDController extends GenericForwardComposer { appendDate(row, order.getInitDate()); appendDate(row, order.getDeadline()); appendCustomer(row, order.getCustomer()); - appendObject(row, order.getTotalBudget()); + appendObject(row, Util.addCurrencySymbol(order.getTotalBudget())); appendObject(row, order.getTotalHours()); appendObject(row, _(order.getState().toString())); appendOperations(row, order); @@ -1306,8 +1343,8 @@ public class OrderCRUDController extends GenericForwardComposer { return orderModel.gettooltipText(order); } - public void reloadTotalBudget(Label txtTotalBudget) { - Util.reloadBindings(txtTotalBudget); + public void reloadTotalBudget(Decimalbox decimalboxTotalBudget) { + Util.reloadBindings(decimalboxTotalBudget); } /** @@ -1685,4 +1722,8 @@ public class OrderCRUDController extends GenericForwardComposer { reloadGridAskedEndDates(); } + public String getMoneyFormat() { + return Util.getMoneyFormat(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java index a01a96076..5df051d86 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java @@ -393,6 +393,7 @@ public class OrderElementTreeController extends TreeController { IOrderElementModel model = orderModel .getOrderElementModel(currentOrderElement); orderElementController.openWindow(model); + updateNameFor(currentOrderElement); updateHoursFor(currentOrderElement); updateBudgetFor(currentOrderElement); } @@ -619,6 +620,7 @@ public class OrderElementTreeController extends TreeController { public void refreshRow(Treeitem item) { try { + getRenderer().updateNameFor((OrderElement) item.getValue()); getRenderer().updateHoursFor((OrderElement) item.getValue()); getRenderer().updateBudgetFor((OrderElement) item.getValue()); getRenderer().render(item, item.getValue()); @@ -742,4 +744,16 @@ public class OrderElementTreeController extends TreeController { }; } + @Override + protected INameHandler getNameHandler() { + return new INameHandler() { + + @Override + public String getNameFor(OrderElement element) { + return element.getName(); + } + + }; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java index fb3123b92..82b3bfb23 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2012 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -59,6 +59,7 @@ import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderLineGroup; import org.libreplan.business.planner.entities.IMoneyCostCalculator; +import org.libreplan.business.planner.entities.PositionConstraintType; import org.libreplan.business.qualityforms.daos.IQualityFormDAO; import org.libreplan.business.qualityforms.entities.QualityForm; import org.libreplan.business.requirements.entities.DirectCriterionRequirement; @@ -909,4 +910,12 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { return false; } + public boolean isAnyTaskWithConstraint(PositionConstraintType type) { + if ((planningState == null) || (planningState.getRootTask() == null)) { + return false; + } + + return planningState.getRootTask().isAnyTaskWithConstraint(type); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java index 8161fd405..63a17ef2a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java @@ -4,7 +4,7 @@ * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * - * Copyright (C) 2011 Igalia S.L + * Copyright (C) 2011-2012 Igalia S.L * * 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 @@ -92,7 +92,11 @@ public abstract class TreeElementOperationsController { public void indentSelectedElement() { if (tree.getSelectedCount() == 1) { + int page = tree.getActivePage(); indent(getSelectedElement()); + if (tree.getPageCount() > page) { + tree.setActivePage(page); + } } else { showSelectAnElementError(); } @@ -236,9 +240,9 @@ class OrderElementOperations extends TreeElementOperationsController extends GenericForwardCo Label lblName = new Label(materialCategory.getName()); Label lblUnits = new Label(getUnits(materialCategory).toString()); - Label lblPrice = new Label(getPrice(materialCategory).toString()); + Label lblPrice = new Label(getPrice(materialCategory).toString() + + getCurrencySymbol()); Treerow tr = null; ti.setValue(node); @@ -573,4 +574,12 @@ public abstract class AssignedMaterialsController extends GenericForwardCo .getId().equals(material.getUnitType().getId()))); } + public String getCurrencySymbol() { + return Util.getCurrencySymbol(); + } + + public String getMoneyFormat() { + return Util.getMoneyFormat(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java index 8fe2839d4..61ba7d08b 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java @@ -60,7 +60,6 @@ import org.libreplan.business.common.entities.ProgressType; import org.libreplan.business.externalcompanies.daos.IExternalCompanyDAO; import org.libreplan.business.labels.entities.Label; import org.libreplan.business.orders.daos.IOrderElementDAO; -import org.libreplan.business.orders.daos.OrderElementDAO; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderStatusEnum; @@ -90,6 +89,7 @@ import org.libreplan.business.workingday.EffortDuration; import org.libreplan.business.workingday.EffortDuration.IEffortFrom; import org.libreplan.business.workingday.IntraDayDate; import org.libreplan.business.workingday.IntraDayDate.PartialDay; +import org.libreplan.web.common.Util; import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -118,41 +118,8 @@ public class TaskElementAdapter { private static final Log LOG = LogFactory.getLog(TaskElementAdapter.class); - private static TaskPositionConstraint getLeftMostFixedDateConstraintAmongChildren( - TaskGroup container) { - - TaskPositionConstraint constraint = null; - for(TaskElement child : ((TaskGroup)container).getChildren()) { - TaskPositionConstraint currentConstraint = null; - if (child instanceof ITaskPositionConstrained) { - ITaskPositionConstrained task = (ITaskPositionConstrained) child; - currentConstraint = task.getPositionConstraint(); - } - else if (child instanceof TaskGroup) { - currentConstraint = getLeftMostFixedDateConstraintAmongChildren( - (TaskGroup) child); - } - if(currentConstraint != null && - currentConstraint.getConstraintType().equals( - PositionConstraintType.START_IN_FIXED_DATE) && - (constraint == null || currentConstraint.getConstraintDate(). - compareTo(constraint.getConstraintDate()) < 0)) { - constraint = currentConstraint; - } - } - return constraint; - } public static List> getStartConstraintsFor( TaskElement taskElement, LocalDate orderInitDate) { - if (taskElement instanceof TaskGroup) { - TaskPositionConstraint constraint = - getLeftMostFixedDateConstraintAmongChildren((TaskGroup) taskElement); - if(constraint == null) { - return Collections.emptyList(); - } - return Collections.singletonList(equalTo(toGantt( - constraint.getConstraintDate()))); - } if (taskElement instanceof ITaskPositionConstrained) { ITaskPositionConstrained task = (ITaskPositionConstrained) taskElement; TaskPositionConstraint startConstraint = task @@ -1067,13 +1034,13 @@ public class TaskElementAdapter { if (taskElement.getOrderElement() instanceof Order) { result.append(_("State") + ": ").append(getOrderState()); } else { + String budget = Util.addCurrencySymbol(getBudget()); + String moneyCost = Util.addCurrencySymbol(getMoneyCost()); result.append( - _("Budget: {0}€, Consumed: {1}€ ({2}%)", - getBudget(), - getMoneyCost(), - getMoneyCostBarPercentage().multiply( - new BigDecimal(100)))).append( - "
"); + _("Budget: {0}, Consumed: {1} ({2}%)", budget, + moneyCost, getMoneyCostBarPercentage() + .multiply(new BigDecimal(100)))) + .append("
"); } String labels = buildLabelsText(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java index eaac867bf..bd25935b7 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java @@ -508,6 +508,12 @@ public abstract class ChartFiller implements IChartFiller { return result; } + protected SortedMap calculatedValueForEveryDay( + SortedMap values, Interval interval) { + return calculatedValueForEveryDay(values, interval.getStart(), + interval.getFinish()); + } + protected SortedMap calculatedValueForEveryDay( SortedMap map, Date start, Date finish) { return calculatedValueForEveryDay(map, new LocalDate(start), diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java index b4dffbd2d..d8a8c7b2f 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -22,18 +22,13 @@ package org.libreplan.web.planner.chart; import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; -import java.util.TreeMap; -import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; import org.libreplan.web.I18nHelper; import org.zkforge.timeplot.Plotinfo; @@ -44,130 +39,24 @@ import org.zkoss.ganttz.util.Interval; /** - * Abstract class with the common functionality for the earned value chart. * * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Abstract class with the common functionality for the earned value + * chart. */ public abstract class EarnedValueChartFiller extends ChartFiller { - public static void forValuesAtSameKey(Map a, Map b, - IOperation onSameKey) { - for (Entry each : a.entrySet()) { - V aValue = each.getValue(); - V bValue = b.get(each.getKey()); - onSameKey.operate(each.getKey(), aValue, bValue); - } - } - public interface IOperation { - - public void operate(K key, V a, V b); - - public void undefinedFor(K key); - } - - protected static abstract class PreconditionChecker implements - IOperation { - - private final IOperation decorated; - - protected PreconditionChecker(IOperation decorated) { - this.decorated = decorated; - } - - @Override - public void operate(K key, V a, V b) { - if (isOperationDefinedFor(key, a, b)) { - decorated.operate(key, a, b); - } else { - decorated.undefinedFor(key); - } - } - - protected abstract boolean isOperationDefinedFor(K key, V a, V b); - - @Override - public void undefinedFor(K key) { - decorated.undefinedFor(key); - } - - } - - public static IOperation notNullOperands( - final IOperation operation) { - return new PreconditionChecker(operation) { - @Override - protected boolean isOperationDefinedFor(K key, V a, V b) { - return a != null && b != null; - } - }; - } - - public static IOperation secondOperandNotZero( - final IOperation operation) { - return new PreconditionChecker(operation) { - @Override - protected boolean isOperationDefinedFor(K key, BigDecimal a, BigDecimal b) { - return b.signum() != 0; - } - }; - } - public static boolean includes(Interval interval, LocalDate date) { LocalDate start = interval.getStart(); LocalDate end = interval.getFinish(); return start.compareTo(date) <= 0 && date.compareTo(end) < 0; } - public enum EarnedValueType { - - BCWS(_("BCWS"), _("Budgeted Cost Work Scheduled"), "#0000FF"), ACWP( - _("ACWP"), _("Actual Cost Work Performed"), "#FF0000"), BCWP( - _("BCWP"), _("Budgeted Cost Work Performed"), "#00FF00"), CV( - _("CV"), _("Cost Variance"), "#FF8800"), SV(_("SV"), - _("Schedule Variance"), "#00FFFF"), BAC(_("BAC"), - _("Budget At Completion"), "#FF00FF"), EAC(_("EAC"), - _("Estimate At Completion"), "#880000"), VAC(_("VAC"), - _("Variance At Completion"), "#000088"), ETC(_("ETC"), - _("Estimate To Complete"), "#008800"), CPI(_("CPI"), - _("Cost Performance Index"), "#888800"), SPI(_("SPI"), - _("Schedule Performance Index"), "#008888") - ; - - /** - * Forces to mark the string as needing translation - */ - private static String _(String string) { - return string; - } - - private String acronym; - private String name; - private String color; - - private EarnedValueType(String acronym, String name, String color) { - this.acronym = acronym; - this.name = name; - this.color = color; - } - - public String getAcronym() { - return I18nHelper._(acronym); - } - - public String getName() { - return I18nHelper._(name); - } - - public String getColor() { - return color; - } - } protected Map> indicators = new HashMap>(); - private Interval indicatorsInterval; - protected abstract void calculateBudgetedCostWorkScheduled(Interval interval); - protected abstract void calculateActualCostWorkPerformed(Interval interval); - protected abstract void calculateBudgetedCostWorkPerformed(Interval interval); + private Interval indicatorsInterval; protected Plotinfo createPlotInfo(SortedMap map, Interval interval, String lineColor) { @@ -203,167 +92,55 @@ public abstract class EarnedValueChartFiller extends ChartFiller { calculateSchedulePerformanceIndex(); } + protected abstract void calculateBudgetedCostWorkScheduled(Interval interval); + + protected abstract void calculateActualCostWorkPerformed(Interval interval); + + protected abstract void calculateBudgetedCostWorkPerformed(Interval interval); + + protected abstract void calculateCostVariance(); + + protected abstract void calculateScheduleVariance(); + + protected abstract void calculateBudgetAtCompletion(); + + protected abstract void calculateEstimateAtCompletion(); + + protected abstract void calculateVarianceAtCompletion(); + + protected abstract void calculateEstimatedToComplete(); + + protected abstract void calculateSchedulePerformanceIndex(); + + protected abstract void calculateCostPerformanceIndex(); + + protected abstract Set getSelectedIndicators(); + + public SortedMap getIndicator(EarnedValueType indicator) { + return indicators.get(indicator); + } + public BigDecimal getIndicator(EarnedValueType indicator, LocalDate date) { return indicators.get(indicator).get(date); } - private void calculateCostVariance() { - // CV = BCWP - ACWP - indicators.put(EarnedValueType.CV, - substract(EarnedValueType.BCWP, EarnedValueType.ACWP)); + public void setIndicator(EarnedValueType type, SortedMap values) { + indicators.put(type, values); } - private void calculateScheduleVariance() { - // SV = BCWP - BCWS - - indicators.put(EarnedValueType.SV, - substract(EarnedValueType.BCWP, EarnedValueType.BCWS)); + public void setIndicatorInInterval(EarnedValueType type, + Interval interval, SortedMap values) { + addZeroBeforeTheFirstValue(values); + indicators.put(type, calculatedValueForEveryDay(values, interval)); } - private void calculateBudgetAtCompletion() { - // BAC = max (BCWS) - SortedMap bac = new TreeMap(); - SortedMap bcws = indicators - .get(EarnedValueType.BCWS); - - BigDecimal value = Collections.max(bcws.values()); - for (LocalDate day : bcws.keySet()) { - bac.put(day, value); + protected void addZeroBeforeTheFirstValue( + SortedMap map) { + if (!map.isEmpty()) { + map.put(map.firstKey().minusDays(1), BigDecimal.ZERO); } - - indicators.put(EarnedValueType.BAC, bac); } - private void calculateEstimateAtCompletion() { - // EAC = (ACWP/BCWP) * BAC - SortedMap dividend = divide( - EarnedValueType.ACWP, EarnedValueType.BCWP, - BigDecimal.ZERO); - SortedMap bac = indicators - .get(EarnedValueType.BAC); - indicators.put(EarnedValueType.EAC, multiply(dividend, bac)); - } - - private static SortedMap multiply( - Map firstFactor, - Map secondFactor) { - final SortedMap result = new TreeMap(); - forValuesAtSameKey(firstFactor, secondFactor, - multiplicationOperation(result)); - return result; - } - - private static IOperation multiplicationOperation( - final SortedMap result) { - return notNullOperands(new IOperation() { - - @Override - public void operate(LocalDate key, BigDecimal a, - BigDecimal b) { - result.put(key, a.multiply(b)); - } - - @Override - public void undefinedFor(LocalDate key) { - result.put(key, BigDecimal.ZERO); - } - }); - } - - private void calculateVarianceAtCompletion() { - indicators.put(EarnedValueType.VAC, - substract(EarnedValueType.BAC, EarnedValueType.EAC)); - } - - private void calculateEstimatedToComplete() { - // ETC = EAC - ACWP - indicators.put(EarnedValueType.ETC, - substract(EarnedValueType.EAC, EarnedValueType.ACWP)); - } - - private SortedMap substract(EarnedValueType minuend, - EarnedValueType subtrahend) { - return substract(indicators.get(minuend), indicators.get(subtrahend)); - } - - private static SortedMap substract( - Map minuend, - Map subtrahend) { - final SortedMap result = new TreeMap(); - forValuesAtSameKey(minuend, subtrahend, substractionOperation(result)); - return result; - } - - private static IOperation substractionOperation( - final SortedMap result) { - return notNullOperands(new IOperation() { - - @Override - public void operate(LocalDate key, BigDecimal minuedValue, - BigDecimal subtrahendValue) { - result.put(key, - minuedValue.subtract(subtrahendValue)); - } - - @Override - public void undefinedFor(LocalDate key) { - } - }); - } - - private void calculateCostPerformanceIndex() { - // CPI = BCWP / ACWP - indicators.put(EarnedValueType.CPI, - divide(EarnedValueType.BCWP, EarnedValueType.ACWP, - BigDecimal.ZERO)); - } - - private void calculateSchedulePerformanceIndex() { - // SPI = BCWP / BCWS - indicators.put(EarnedValueType.SPI, - divide(EarnedValueType.BCWP, EarnedValueType.BCWS, - BigDecimal.ZERO)); - } - - private SortedMap divide(EarnedValueType dividend, - EarnedValueType divisor, BigDecimal defaultIfNotComputable) { - Validate.notNull(indicators.get(dividend)); - Validate.notNull(indicators.get(divisor)); - return divide(indicators.get(dividend), indicators.get(divisor), - defaultIfNotComputable); - } - - private static SortedMap divide( - Map dividend, - Map divisor, - final BigDecimal defaultIfNotComputable) { - final TreeMap result = new TreeMap(); - forValuesAtSameKey(dividend, divisor, - divisionOperation(result, defaultIfNotComputable)); - return result; - } - - private static IOperation divisionOperation( - final TreeMap result, - final BigDecimal defaultIfNotComputable) { - return notNullOperands(secondOperandNotZero(new IOperation() { - - @Override - public void operate(LocalDate key, BigDecimal dividendValue, - BigDecimal divisorValue) { - result.put(key, dividendValue.divide(divisorValue, - RoundingMode.DOWN)); - } - - @Override - public void undefinedFor(LocalDate key) { - result.put(key, defaultIfNotComputable); - } - })); - } - - protected abstract Set getSelectedIndicators(); - @Override public void fillChart(Timeplot chart, Interval interval, Integer size) { chart.getChildren().clear(); @@ -411,11 +188,55 @@ public abstract class EarnedValueChartFiller extends ChartFiller { return includes(chartInterval, today) ? today : chartInterval .getFinish().minusDays(1); } - protected void addZeroBeforeTheFirstValue( - SortedMap map) { - if (!map.isEmpty()) { - map.put(map.firstKey().minusDays(1), BigDecimal.ZERO); + + /** + * + * @author Manuel Rego Casasnovas + * + */ + public enum EarnedValueType { + + BCWS(_("BCWS"), _("Budgeted Cost Work Scheduled"), "#0000FF"), ACWP( + _("ACWP"), _("Actual Cost Work Performed"), "#FF0000"), BCWP( + _("BCWP"), _("Budgeted Cost Work Performed"), "#00FF00"), CV( + _("CV"), _("Cost Variance"), "#FF8800"), SV(_("SV"), + _("Schedule Variance"), "#00FFFF"), BAC(_("BAC"), + _("Budget At Completion"), "#FF00FF"), EAC(_("EAC"), + _("Estimate At Completion"), "#880000"), VAC(_("VAC"), + _("Variance At Completion"), "#000088"), ETC(_("ETC"), + _("Estimate To Complete"), "#008800"), CPI(_("CPI"), + _("Cost Performance Index"), "#888800"), SPI(_("SPI"), + _("Schedule Performance Index"), "#008888") + ; + + /** + * Forces to mark the string as needing translation + */ + private static String _(String string) { + return string; + } + + private String acronym; + private String name; + private String color; + + private EarnedValueType(String acronym, String name, String color) { + this.acronym = acronym; + this.name = name; + this.color = color; + } + + public String getAcronym() { + return I18nHelper._(acronym); + } + + public String getName() { + return I18nHelper._(name); + } + + public String getColor() { + return color; } } -} +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java index eebdf859b..b5c52f78e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java @@ -38,8 +38,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; import org.joda.time.LocalDate; import org.libreplan.business.calendars.entities.AvailabilityTimeLine; @@ -55,6 +53,7 @@ import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderStatusEnum; import org.libreplan.business.planner.chart.ILoadChartData; import org.libreplan.business.planner.chart.ResourceLoadChartData; +import org.libreplan.business.planner.entities.ICompanyEarnedValueCalculator; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.TaskGroup; import org.libreplan.business.planner.entities.TaskMilestone; @@ -62,7 +61,6 @@ import org.libreplan.business.scenarios.IScenarioManager; import org.libreplan.business.scenarios.entities.Scenario; import org.libreplan.business.users.daos.IUserDAO; import org.libreplan.business.users.entities.User; -import org.libreplan.business.workreports.entities.WorkReportLine; import org.libreplan.web.planner.TaskElementAdapter; import org.libreplan.web.planner.TaskGroupPredicate; import org.libreplan.web.planner.chart.Chart; @@ -132,6 +130,9 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { @Autowired private IAdHocTransactionService transactionService; + @Autowired + private ICompanyEarnedValueCalculator earnedValueCalculator; + private List keepAliveZoomListeners = new ArrayList(); private List earnedValueChartConfigurationCheckboxes = new ArrayList(); @@ -275,6 +276,7 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { setupChart(chartLoadTimeplot, new CompanyLoadChartFiller(), planner); chartComponent.getTabs().getLastChild().addEventListener(Events.ON_SELECT, new EventListener() { + @Override public void onEvent(Event event) throws Exception { createOnDemandEarnedValueTimePlot(chartComponent, planner); event.getTarget().removeEventListener(Events.ON_SELECT, this); @@ -783,75 +785,101 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { } + /** + * + * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Calculates 'Earned Value' indicators and set them in the Company + * 'Earned Valued' chart + * + */ private class CompanyEarnedValueChartFiller extends EarnedValueChartFiller { + @Override protected void calculateBudgetedCostWorkScheduled(Interval interval) { - Map> estimatedCostPerTask = - databaseSnapshots.snapshotEstimatedCostPerTask(); - Collection list = filterTasksByDate( - estimatedCostPerTask.keySet(), getFilterInterval()); - - SortedMap estimatedCost = new TreeMap(); - - for (TaskElement taskElement : list) { - addCost(estimatedCost, estimatedCostPerTask.get(taskElement)); - } - - estimatedCost = accumulateResult(estimatedCost); - addZeroBeforeTheFirstValue(estimatedCost); - indicators.put(EarnedValueType.BCWS, calculatedValueForEveryDay( - estimatedCost, interval.getStart(), interval.getFinish())); + setIndicatorInInterval(EarnedValueType.BCWS, interval, + earnedValueCalculator + .calculateBudgetedCostWorkScheduled(getFilterInterval())); } + @Override protected void calculateActualCostWorkPerformed(Interval interval) { - SortedMap workReportCost = getWorkReportCost(); - - workReportCost = accumulateResult(workReportCost); - addZeroBeforeTheFirstValue(workReportCost); - indicators.put(EarnedValueType.ACWP, calculatedValueForEveryDay( - workReportCost, interval.getStart(), interval.getFinish())); - } - - private SortedMap getWorkReportCost() { - SortedMap result = new TreeMap(); - - Collection workReportLines = filterWorkReportLinesByDate( - databaseSnapshots.snapshotWorkReportLines(), - getFilterInterval()); - - if (workReportLines.isEmpty()) { - return result; - } - - for (WorkReportLine workReportLine : workReportLines) { - LocalDate day = new LocalDate(workReportLine.getDate()); - BigDecimal cost = workReportLine.getEffort() - .toHoursAsDecimalWithScale(2); - - if (!result.containsKey(day)) { - result.put(day, BigDecimal.ZERO); - } - result.put(day, result.get(day).add(cost)); - } - - return result; + setIndicatorInInterval(EarnedValueType.ACWP, interval, + earnedValueCalculator + .calculateActualCostWorkPerformed(getFilterInterval())); } + @Override protected void calculateBudgetedCostWorkPerformed(Interval interval) { - Map> advanceCostPerTask = - databaseSnapshots.snapshotAdvanceCostPerTask(); - Collection list = filterTasksByDate( - advanceCostPerTask.keySet(), getFilterInterval()); + setIndicatorInInterval(EarnedValueType.BCWP, interval, + earnedValueCalculator + .calculateBudgetedCostWorkPerformed(getFilterInterval())); + } - SortedMap advanceCost = new TreeMap(); + @Override + protected void calculateCostVariance() { + setIndicator(EarnedValueType.CV, + earnedValueCalculator.calculateCostVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); + } - for (TaskElement taskElement : list) { - addCost(advanceCost, advanceCostPerTask.get(taskElement)); - } + @Override + protected void calculateScheduleVariance() { + setIndicator(EarnedValueType.SV, + earnedValueCalculator.calculateScheduleVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); + } - addZeroBeforeTheFirstValue(advanceCost); - indicators.put(EarnedValueType.BCWP, calculatedValueForEveryDay( - advanceCost, interval.getStart(), interval.getFinish())); + @Override + protected void calculateSchedulePerformanceIndex() { + setIndicator(EarnedValueType.SPI, + earnedValueCalculator.calculateSchedulePerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); + } + + @Override + protected void calculateBudgetAtCompletion() { + setIndicator( + EarnedValueType.BAC, + earnedValueCalculator + .calculateBudgetAtCompletion(getIndicator(EarnedValueType.BCWS))); + } + + @Override + protected void calculateEstimateAtCompletion() { + setIndicator(EarnedValueType.EAC, + earnedValueCalculator.calculateEstimateAtCompletion( + getIndicator(EarnedValueType.ACWP), + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BAC))); + } + + @Override + protected void calculateVarianceAtCompletion() { + setIndicator(EarnedValueType.VAC, + earnedValueCalculator.calculateVarianceAtCompletion( + getIndicator(EarnedValueType.BAC), + getIndicator(EarnedValueType.EAC))); + } + + @Override + protected void calculateEstimatedToComplete() { + setIndicator(EarnedValueType.ETC, + earnedValueCalculator.calculateEstimatedToComplete( + getIndicator(EarnedValueType.EAC), + getIndicator(EarnedValueType.ACWP))); + } + + @Override + protected void calculateCostPerformanceIndex() { + setIndicator(EarnedValueType.CPI, + earnedValueCalculator.calculateCostPerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); } @Override @@ -859,33 +887,9 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { return getEarnedValueSelectedIndicators(); } - private List filterTasksByDate( - Collection tasks, - AvailabilityTimeLine.Interval interval) { - List result = new ArrayList(); - for(TaskElement task : tasks) { - if (interval.includes(task.getStartAsLocalDate()) - || interval.includes(task.getEndAsLocalDate())) { - result.add(task); - } - } - return result; - } - - - private List filterWorkReportLinesByDate( - Collection lines, - AvailabilityTimeLine.Interval interval) { - List result = new ArrayList(); - for(WorkReportLine line: lines) { - if (interval.includes(line.getLocalDate())) { - result.add(line); - } - } - return result; - } } + @Override @Transactional(readOnly=true) public ProgressType getProgressTypeFromConfiguration() { return configurationDAO.getConfiguration().getProgressType(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/consolidations/AdvanceConsolidationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/consolidations/AdvanceConsolidationModel.java index f08e345ee..bf45220f3 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/consolidations/AdvanceConsolidationModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/consolidations/AdvanceConsolidationModel.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -57,7 +57,6 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.zkoss.ganttz.data.GanttDate; import org.zkoss.ganttz.extensions.IContextWithPlannerTask; /** @@ -152,7 +151,6 @@ public class AdvanceConsolidationModel implements IAdvanceConsolidationModel { updateConsolidationInAdvanceIfIsNeeded(); ganttTask.enforceDependenciesDueToPositionPotentiallyModified(); - ganttTask.reloadResourcesText(); context.reloadCharts(); } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java index 8d058fa1c..9c04f7c78 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java @@ -38,8 +38,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; import org.apache.commons.lang.Validate; import org.apache.commons.logging.Log; @@ -51,7 +49,6 @@ import org.libreplan.business.common.AdHocTransactionService; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; import org.libreplan.business.common.Registry; -import org.libreplan.business.common.entities.ProgressType; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.entities.HoursGroup; @@ -64,7 +61,7 @@ import org.libreplan.business.planner.chart.ResourceLoadChartData; import org.libreplan.business.planner.entities.DayAssignment; import org.libreplan.business.planner.entities.DayAssignment.FilterType; import org.libreplan.business.planner.entities.ICostCalculator; -import org.libreplan.business.planner.entities.Task; +import org.libreplan.business.planner.entities.IOrderEarnedValueCalculator; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.TaskGroup; import org.libreplan.business.resources.entities.CriterionSatisfaction; @@ -140,7 +137,6 @@ import org.zkoss.zul.Div; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Messagebox; -import org.zkoss.zul.Progressmeter; import org.zkoss.zul.Tab; import org.zkoss.zul.Tabbox; import org.zkoss.zul.Tabpanel; @@ -152,6 +148,7 @@ import org.zkoss.zul.Vbox; * @author Óscar González Fernández * @author Manuel Rego Casasnovas * @author Lorenzo Tilve Álvaro + * @author Diego Pino García */ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -262,14 +259,15 @@ public class OrderPlanningModel implements IOrderPlanningModel { @Autowired private ICostCalculator hoursCostCalculator; + @Autowired + private IOrderEarnedValueCalculator earnedValueCalculator; + private List earnedValueChartConfigurationCheckboxes = new ArrayList(); private List keepAliveChartVisibilityListeners = new ArrayList(); private Planner planner; - private OverAllProgressContent overallProgressContent; - private String tabSelected = "load_tab"; private static class NullSeparatorCommandOnTask implements @@ -387,11 +385,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { this.earnedValueChartFiller = createOrderEarnedValueChartFiller(planner.getTimeTracker()); chartTabpanels.appendChild(createEarnedValueTab(chartEarnedValueTimeplot, earnedValueChartFiller)); - // Create 'Overall progress' tab - Hbox chartOverallProgressTimeplot = new Hbox(); - Tabpanel overallProgressTab = createOverallProgressTab(chartOverallProgressTimeplot); - chartTabpanels.appendChild(overallProgressTab); - // Append tab panels chartComponent.appendChild(chartTabpanels); ChangeHooker changeHooker = new ChangeHooker(configuration, saveCommand); @@ -399,45 +392,38 @@ public class OrderPlanningModel implements IOrderPlanningModel { setupLoadChart(chartLoadTimeplot, planner, changeHooker); setupEarnedValueChart(chartEarnedValueTimeplot, earnedValueChartFiller, planner, changeHooker); - setupOverallProgress(planner, changeHooker); setupAdvanceAssignmentPlanningController(planner, advanceAssignmentPlanningController); PROFILING_LOG .info("preparing charts and miscellaneous took: " + (System.currentTimeMillis() - preparingChartsAndMisc) + " ms"); + // Calculate critical path progress, needed for 'Project global progress' chart in Dashboard view planner.addGraphChangeListenersFromConfiguration(configuration); + updateCriticalPathProgress(); long overalProgressContentTime = System.currentTimeMillis(); - overallProgressContent = new OverAllProgressContent(overallProgressTab); - overallProgressContent.updateAndRefresh(); PROFILING_LOG.info("overalProgressContent took: " + (System.currentTimeMillis() - overalProgressContentTime)); } + /** + * First time a project is loaded, it's needed to calculate the theoretical + * critical path progress and real critical path progress. These values are + * later updated whenever the project is saved + */ + private void updateCriticalPathProgress() { + if (planningState.isEmpty() || planner == null) { + return; + } + TaskGroup rootTask = planningState.getRootTask(); + rootTask.updateCriticalPathProgress(planner.getCriticalPath()); + } + private OrderEarnedValueChartFiller earnedValueChartFiller; private void setupAdvanceAssignmentPlanningController(final Planner planner, AdvanceAssignmentPlanningController advanceAssignmentPlanningController) { - advanceAssignmentPlanningController.reloadOverallProgressListener(new IReloadChartListener() { - - @Override - public void reloadChart() { - Registry.getTransactionService().runOnReadOnlyTransaction(new IOnTransaction() { - - @Override - public Void execute() { - if (isExecutingOutsideZKExecution()) { - return null; - } - if (planner.isVisibleChart()) { - overallProgressContent.updateAndRefresh(); - } - return null; - } - }); - } - }); advanceAssignmentPlanningController.setReloadEarnedValueListener(new IReloadChartListener() { @Override @@ -462,15 +448,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { }); } - private Tabpanel createOverallProgressTab( - Hbox chartOverallProgressTimeplot) { - Tabpanel result = new Tabpanel(); - org.zkoss.zk.ui.Component component = Executions.createComponents( - "/planner/_tabPanelOverallProgress.zul", result, null); - component.setParent(result); - return result; - } - private Timeplot createEmptyTimeplot() { Timeplot timeplot = new Timeplot(); timeplot.appendChild(new Plotinfo()); @@ -576,24 +553,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { setEventListenerConfigurationCheckboxes(earnedValueChart); } - private void setupOverallProgress(final Planner planner, - ChangeHooker changeHooker) { - - changeHooker.withReadOnlyTransactionWraping().hookInto( - EnumSet.allOf(ChangeTypes.class), new IReloadChartListener() { - - @Override - public void reloadChart() { - if (isExecutingOutsideZKExecution()) { - return; - } - if (planner.isVisibleChart()) { - overallProgressContent.updateAndRefresh(); - } - } - }); - } - enum ChangeTypes { ON_SAVE, ON_RELOAD_CHART_REQUESTED, ON_GRAPH_CHANGED; } @@ -763,8 +722,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { Tabs chartTabs = new Tabs(); chartTabs.appendChild(createTab(_("Load"), "load_tab")); chartTabs.appendChild(createTab(_("Earned value"), "earned_value_tab")); - chartTabs.appendChild(createTab(_("Overall progress"), - "overall_progress_tab")); chartComponent.appendChild(chartTabs); chartTabs.setSclass("charts-tabbox"); @@ -1130,6 +1087,7 @@ public class OrderPlanningModel implements IOrderPlanningModel { | Messagebox.CANCEL, Messagebox.QUESTION, new org.zkoss.zk.ui.event.EventListener() { + @Override public void onEvent(Event evt) throws InterruptedException { if (evt.getName().equals("onOK")) { @@ -1386,6 +1344,15 @@ public class OrderPlanningModel implements IOrderPlanningModel { } + /** + * + * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Calculates 'Earned Value' indicators and set them in the Order + * 'Earned Valued' chart + * + */ class OrderEarnedValueChartFiller extends EarnedValueChartFiller { private Order order; @@ -1394,63 +1361,90 @@ public class OrderPlanningModel implements IOrderPlanningModel { this.order = orderReloaded; } + @Override protected void calculateBudgetedCostWorkScheduled(Interval interval) { - List list = order - .getAllChildrenAssociatedTaskElements(); - list.add(order.getAssociatedTaskElement()); - - SortedMap estimatedCost = new TreeMap(); - - for (TaskElement taskElement : list) { - if (taskElement instanceof Task) { - addCost(estimatedCost, hoursCostCalculator - .getEstimatedCost((Task) taskElement)); - } - } - - estimatedCost = accumulateResult(estimatedCost); - addZeroBeforeTheFirstValue(estimatedCost); - indicators.put(EarnedValueType.BCWS, calculatedValueForEveryDay( - estimatedCost, interval.getStart(), interval.getFinish())); + setIndicatorInInterval(EarnedValueType.BCWS, interval, + earnedValueCalculator + .calculateBudgetedCostWorkScheduled(order)); } + @Override protected void calculateActualCostWorkPerformed(Interval interval) { - List list = order - .getAllChildrenAssociatedTaskElements(); - list.add(order.getAssociatedTaskElement()); - - SortedMap workReportCost = new TreeMap(); - - for (TaskElement taskElement : list) { - if (taskElement instanceof Task) { - addCost(workReportCost, hoursCostCalculator - .getWorkReportCost((Task) taskElement)); - } - } - - workReportCost = accumulateResult(workReportCost); - addZeroBeforeTheFirstValue(workReportCost); - indicators.put(EarnedValueType.ACWP, calculatedValueForEveryDay( - workReportCost, interval.getStart(), interval.getFinish())); + setIndicatorInInterval(EarnedValueType.ACWP, interval, + earnedValueCalculator + .calculateActualCostWorkPerformed(order)); } + @Override protected void calculateBudgetedCostWorkPerformed(Interval interval) { - List list = order - .getAllChildrenAssociatedTaskElements(); - list.add(order.getAssociatedTaskElement()); + setIndicatorInInterval(EarnedValueType.BCWP, interval, + earnedValueCalculator + .calculateBudgetedCostWorkPerformed(order)); + } - SortedMap advanceCost = new TreeMap(); + @Override + protected void calculateCostVariance() { + setIndicator(EarnedValueType.CV, + earnedValueCalculator.calculateCostVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); + } - for (TaskElement taskElement : list) { - if (taskElement instanceof Task) { - addCost(advanceCost, hoursCostCalculator - .getAdvanceCost((Task) taskElement)); - } - } + @Override + protected void calculateScheduleVariance() { + setIndicator(EarnedValueType.SV, + earnedValueCalculator.calculateScheduleVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); + } - addZeroBeforeTheFirstValue(advanceCost); - indicators.put(EarnedValueType.BCWP, calculatedValueForEveryDay( - advanceCost, interval.getStart(), interval.getFinish())); + @Override + protected void calculateBudgetAtCompletion() { + setIndicator( + EarnedValueType.BAC, + earnedValueCalculator + .calculateBudgetAtCompletion(getIndicator(EarnedValueType.BCWS))); + } + + @Override + protected void calculateEstimateAtCompletion() { + setIndicator(EarnedValueType.EAC, + earnedValueCalculator.calculateEstimateAtCompletion( + getIndicator(EarnedValueType.ACWP), + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BAC))); + } + + @Override + protected void calculateVarianceAtCompletion() { + setIndicator(EarnedValueType.VAC, + earnedValueCalculator.calculateVarianceAtCompletion( + getIndicator(EarnedValueType.BAC), + getIndicator(EarnedValueType.EAC))); + } + + @Override + protected void calculateEstimatedToComplete() { + setIndicator(EarnedValueType.ETC, + earnedValueCalculator.calculateEstimatedToComplete( + getIndicator(EarnedValueType.EAC), + getIndicator(EarnedValueType.ACWP))); + } + + @Override + protected void calculateCostPerformanceIndex() { + setIndicator(EarnedValueType.CPI, + earnedValueCalculator.calculateCostPerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); + } + + @Override + protected void calculateSchedulePerformanceIndex() { + setIndicator(EarnedValueType.SPI, + earnedValueCalculator.calculateSchedulePerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); } @Override @@ -1466,6 +1460,7 @@ public class OrderPlanningModel implements IOrderPlanningModel { return subcontractCommand; } + @Override public Order getOrder() { return planningState.getOrder(); } @@ -1505,149 +1500,4 @@ public class OrderPlanningModel implements IOrderPlanningModel { } } - /** - * - * @author Diego Pino García - * - * Helper class to show the content of a OverallProgress panel - * - */ - private class OverAllProgressContent { - - private Progressmeter progressCriticalPathByDuration; - - private Label lbCriticalPathByDuration; - - private Progressmeter progressCriticalPathByNumHours; - - private Label lbCriticalPathByNumHours; - - private Progressmeter progressSpread; - - private Label lbProgressSpread; - - private Progressmeter progressAllByNumHours; - - private Label lbProgressAllByNumHours; - - public OverAllProgressContent(Tabpanel tabpanel) { - initializeProgressCriticalPathByDuration(tabpanel); - initializeProgressCriticalPathByNumHours(tabpanel); - initializeProgressSpread(tabpanel); - initializeProgressAllByNumHours(tabpanel); - - tabpanel.setVariable("overall_progress_content", this, true); - } - - private void initializeProgressCriticalPathByNumHours(Tabpanel tabpanel) { - progressCriticalPathByNumHours = (Progressmeter) tabpanel - .getFellow("progressCriticalPathByNumHours"); - lbCriticalPathByNumHours = (Label) tabpanel - .getFellow("lbCriticalPathByNumHours"); - ((Label) tabpanel.getFellow("textCriticalPathByNumHours")) - .setValue(_(ProgressType.CRITICAL_PATH_NUMHOURS.toString())); - } - - private void initializeProgressCriticalPathByDuration(Tabpanel tabpanel) { - progressCriticalPathByDuration = (Progressmeter) tabpanel - .getFellow("progressCriticalPathByDuration"); - lbCriticalPathByDuration = (Label) tabpanel - .getFellow("lbCriticalPathByDuration"); - ((Label) tabpanel.getFellow("textCriticalPathByDuration")) - .setValue(_(ProgressType.CRITICAL_PATH_DURATION.toString())); - } - - public void initializeProgressSpread(Tabpanel tabpanel) { - progressSpread = (Progressmeter) tabpanel - .getFellow("progressSpread"); - lbProgressSpread = (Label) tabpanel.getFellow("lbProgressSpread"); - ((Label) tabpanel.getFellow("textProgressSpread")) - .setValue(_(ProgressType.SPREAD_PROGRESS.toString())); - } - - public void initializeProgressAllByNumHours(Tabpanel tabpanel) { - progressAllByNumHours = (Progressmeter) tabpanel - .getFellow("progressAllByNumHours"); - lbProgressAllByNumHours = (Label) tabpanel - .getFellow("lbProgressAllByNumHours"); - ((Label) tabpanel.getFellow("textProgressAllByNumHours")) - .setValue(_(ProgressType.ALL_NUMHOURS.toString())); - } - - public void refresh() { - if (planningState.isEmpty()) { - return; - } - TaskGroup rootTask = planningState.getRootTask(); - - setProgressSpread(rootTask.getAdvancePercentage()); - setProgressAllByNumHours(rootTask.getProgressAllByNumHours()); - setCriticalPathByDuration(rootTask - .getCriticalPathProgressByDuration()); - setCriticalPathByNumHours(rootTask - .getCriticalPathProgressByNumHours()); - } - - private void updateAndRefresh() { - if (planningState.isEmpty()) { - return; - } - update(); - refresh(); - } - - private void update() { - TaskGroup rootTask = planningState.getRootTask(); - updateCriticalPathProgress(rootTask); - } - - private void updateCriticalPathProgress(TaskGroup rootTask) { - if (planner != null) { - rootTask.updateCriticalPathProgress((List) planner - .getCriticalPath()); - } - } - - private void setProgressSpread(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbProgressSpread.setValue(value.toString() + " %"); - progressSpread.setValue(value.intValue()); - } - - private void setProgressAllByNumHours(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbProgressAllByNumHours.setValue(value.toString() + " %"); - progressAllByNumHours.setValue(value.intValue()); - } - - public void setCriticalPathByDuration(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbCriticalPathByDuration.setValue(value.toString() + " %"); - progressCriticalPathByDuration.setValue(value.intValue()); - } - - public void setCriticalPathByNumHours(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbCriticalPathByNumHours.setValue(value.toString() + " %"); - progressCriticalPathByNumHours.setValue(value.intValue()); - } - - } - } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java index 264102e85..8e81d8dce 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java @@ -227,6 +227,7 @@ public class PlanningStateCreator { } } PlanningState result = createPlanning(reload(order)); + result.onRetrieval(); desktop.setAttribute(ATTRIBUTE_NAME, result); return result; } @@ -1147,14 +1148,13 @@ public class PlanningStateCreator { } } - public static class RelatedWithAnyOf implements + public static class RelatedWith implements IAllocationCriteria { - private final Collection anyOf; + private final Criterion criterion; - public RelatedWithAnyOf( - Collection anyOf) { - this.anyOf = anyOf; + public RelatedWith(Criterion criterion) { + this.criterion = criterion; } @Override @@ -1170,7 +1170,7 @@ public class PlanningStateCreator { private boolean someCriterionIn( Collection allocationCriterions) { for (Criterion each : allocationCriterions) { - if (this.anyOf.contains(each)) { + if (criterion.equals(each)) { return true; } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java index 2a7294deb..c57c529b8 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java @@ -347,4 +347,8 @@ public class SubcontractController extends GenericForwardComposer { public void refressGridEndDates() { Util.reloadBindings(gridEndDates); } + + public String getMoneyFormat() { + return Util.getMoneyFormat(); + } } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MonteCarloTabCreator.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MonteCarloTabCreator.java index def10b900..344763e77 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MonteCarloTabCreator.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MonteCarloTabCreator.java @@ -202,6 +202,7 @@ public class MonteCarloTabCreator { asLocalDate(order.getDeadline()), resourcesSearcher); GanttDiagramGraph graph = createFor( order, adapter); + graph.addTask(order.getAssociatedTaskElement()); graph.addTasks(order.getAllChildrenAssociatedTaskElements()); addDependencies(graph, order); return criticalPathCalculator.calculateCriticalPath(graph); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/AdvancedAllocationTaskController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/AdvancedAllocationTaskController.java index d58b10d7f..e5ead90be 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/AdvancedAllocationTaskController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/AdvancedAllocationTaskController.java @@ -1,7 +1,7 @@ /* * This file is part of LibrePlan * - * Copyright (C) 2011 Igalia, S.L. + * Copyright (C) 2011-2012 Igalia, S.L. * * 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 @@ -158,7 +158,6 @@ public class AdvancedAllocationTaskController extends GenericForwardComposer { private void askForReloads() { if (context != null) { - context.getTask().reloadResourcesText(); context.reloadCharts(); } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java index 71083fc71..236e95cfd 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -39,6 +39,7 @@ import org.libreplan.web.planner.taskedition.TaskPropertiesController.ResourceAl import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; +import org.zkoss.ganttz.TaskComponent; import org.zkoss.ganttz.extensions.IContextWithPlannerTask; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; @@ -310,8 +311,12 @@ public class EditTaskController extends GenericForwardComposer { private void askForReloads() { if (context != null) { - context.getTask().reloadResourcesText(); + org.zkoss.ganttz.data.Task.reloadResourcesText(context); context.reloadCharts(); + if (context.getRelativeTo() instanceof TaskComponent) { + ((TaskComponent) context.getRelativeTo()).updateProperties(); + ((TaskComponent) context.getRelativeTo()).invalidate(); + } } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java index d1363bd78..38a2d931a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -695,4 +695,9 @@ public class TaskPropertiesController extends GenericForwardComposer { public void refreshTaskDeadline() { Util.reloadBindings(deadLineDateBox); } + + public String getMoneyFormat() { + return Util.getMoneyFormat(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/reports/IOrderCostsPerResourceModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/reports/IOrderCostsPerResourceModel.java index cae9a2dda..bfca7cdef 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/reports/IOrderCostsPerResourceModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/reports/IOrderCostsPerResourceModel.java @@ -68,4 +68,5 @@ public interface IOrderCostsPerResourceModel { String getSelectedCriteria(); String getSelectedLabel(); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceController.java b/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceController.java index 2d43abad8..a9e5cf442 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceController.java @@ -88,6 +88,7 @@ public class OrderCostsPerResourceController extends LibrePlanReportController { result.put("endingDate", getEndingDate()); result.put("criteria", getParameterCriterions()); result.put("labels", getParameterLabels()); + result.put("currencySymbol", Util.getCurrencySymbol()); return result; } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceModel.java index 65787f719..7f832e140 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/reports/OrderCostsPerResourceModel.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * 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 @@ -36,6 +36,7 @@ import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JREmptyDataSource; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; +import org.libreplan.business.common.daos.IConfigurationDAO; import org.libreplan.business.costcategories.entities.TypeOfWorkHours; import org.libreplan.business.labels.daos.ILabelDAO; import org.libreplan.business.labels.entities.Label; @@ -61,6 +62,7 @@ import org.springframework.transaction.annotation.Transactional; /** * @author Lorenzo Tilve Álvaro * @author Susana Montes Pedreira + * @author Manuel Rego Casasnovas */ @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -78,6 +80,9 @@ public class OrderCostsPerResourceModel implements IOrderCostsPerResourceModel { @Autowired private ICriterionTypeDAO criterionTypeDAO; + @Autowired + private IConfigurationDAO configurationDAO; + private List selectedOrders = new ArrayList(); private List