Improved: Convert ProjectServices.xml mini-lang to groovyDSL (OFBIZ-13078)

Convert following services :
 * createTimeEntryInTimesheet
 * updateTimeEntryByWorkEffort
 * getProject
 * createProject
 * updateProject
 * updateProjectRole
 * createProjectTask
 * copyProject
 * copyProjectToTemplate
 * scheduleProject
 * getProjectPhaseList
 * getProjectTaskList
 * getProjectTask
 * getProjectsByParties
 * getTasksByParties
 * updateTaskAndRelatedInfo
 * updateTaskAssigment
 * addProjectTimeToNewInvoice
 * addProjectTimeToInvoice
 * addValidationPartiesToTask
diff --git a/projectmgr/minilang/ProjectServices.xml b/projectmgr/minilang/ProjectServices.xml
deleted file mode 100644
index bb756b2..0000000
--- a/projectmgr/minilang/ProjectServices.xml
+++ /dev/null
@@ -1,1531 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
--->
-
-<simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xmlns="http://ofbiz.apache.org/Simple-Method" xsi:schemaLocation="http://ofbiz.apache.org/Simple-Method http://ofbiz.apache.org/dtds/simple-methods.xsd">
-
-    <simple-method method-name="updateProject" short-description="Update a project">
-        <set-service-fields service-name="updateWorkEffort" map="parameters" to-map="updWorkEffort"/>
-        <call-service service-name="updateWorkEffort" in-map-name="updWorkEffort"/>
-        <if-not-empty field="parameters.organizationPartyId">
-            <set field="updProjectRole.newPartyId" from-field="parameters.organizationPartyId"/>
-            <set field="updProjectRole.roleTypeId" value="INTERNAL_ORGANIZATIO"/>
-            <call-simple-method method-name="updProjectRole"/>
-        </if-not-empty>
-        <if-not-empty field="parameters.clientBillingPartyId">
-            <set field="updProjectRole.newPartyId" from-field="parameters.clientBillingPartyId"/>
-            <set field="updProjectRole.roleTypeId" value="CLIENT_BILLING"/>
-            <call-simple-method method-name="updProjectRole"/>
-        </if-not-empty>
-        <if-not-empty field="parameters.emailAddress">
-            <if-validate-method field="parameters.emailAddress" method="isEmail">
-                <else><add-error><fail-property resource="PartyUiLabels" property="PartyEmailAddressNotFormattedCorrectly"/></add-error></else>
-            </if-validate-method>
-            <check-errors/>
-            
-            <!-- find exist e-mail address. if have not exist then create new-->
-            <entity-condition entity-name="WorkEffortContactMechView" list="existEmailAddresses">
-                <condition-list combine="and">
-                    <condition-expr field-name="workEffortId" from-field="parameters.workEffortId"/>
-                    <condition-expr field-name="contactMechTypeId" value="EMAIL_ADDRESS"/>
-                    <condition-expr field-name="infoString" from-field="parameters.emailAddress" ignore-case="true"/>
-                </condition-list>
-            </entity-condition>
-            <filter-list-by-date list="existEmailAddresses"/>
-            <if-empty field="existEmailAddresses">
-                <!-- expire old work effort's e-mail address -->
-                <entity-and list="oldEmailAddresses" entity-name="WorkEffortContactMechView">
-                    <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-                    <field-map field-name="contactMechTypeId" value="EMAIL_ADDRESS"/>
-                </entity-and>
-                <filter-list-by-date list="oldEmailAddresses"/>
-                <iterate list="oldEmailAddresses" entry="oldEmailAddress">
-                    <entity-and list="oldWorkEffortContactMechs" entity-name="WorkEffortContactMech">
-                        <field-map field-name="workEffortId" from-field="oldEmailAddress.workEffortId"/>
-                        <field-map field-name="contactMechId" from-field="oldEmailAddress.contactMechId"/>
-                    </entity-and>
-                    <first-from-list list="oldWorkEffortContactMechs" entry="oldWorkEffortContactMech"/>
-                    <now-timestamp field="oldWorkEffortContactMech.thruDate"/>
-                    <store-value value-field="oldWorkEffortContactMech"/>
-                </iterate>
-                
-                <!-- create new work effort's e-mail address -->
-                <set field="emailAddressIn.emailAddress" from-field="parameters.emailAddress"/>
-                <set field="emailAddressIn.contactMechTypeId" value="EMAIL_ADDRESS"/>
-                <call-service service-name="createEmailAddress" in-map-name="emailAddressIn">
-                    <result-to-field result-name="contactMechId"/>
-                </call-service>
-                <set field="workEffortContactMechIn.workEffortId" from-field="parameters.workEffortId"/>
-                <set field="workEffortContactMechIn.contactMechId" from-field="contactMechId"/>
-                <set field="workEffortContactMechIn.contactMechTypeId" value="EMAIL_ADDRESS"/>
-                <set field="workEffortContactMechIn.infoString" from-field="parameters.emailAddress"/>
-                <call-service service-name="createWorkEffortContactMech" in-map-name="workEffortContactMechIn"/>
-            </if-empty>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="updProjectRole"
-        short-description="update/create a specif role and type for a project
-                            input map: updProjectRole.newPartyId, updProjectRole.roleTypeId">
-        <entity-and entity-name="WorkEffortPartyAssignment" list="workEffortPartyAssignments" filter-by-date="true">
-            <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-            <field-map field-name="roleTypeId" from-field="updProjectRole.roleTypeId"/>
-        </entity-and>
-        <!-- end current record if required -->
-        <if-not-empty field="workEffortPartyAssignments">
-            <first-from-list list="workEffortPartyAssignments" entry="workEffortPartyAssignment"/>
-            <if-compare-field field="updProjectRole.newPartyId" operator="not-equals" to-field="workEffortPartyAssignment.partyId">
-                <now-timestamp field="workEffortPartyAssignment.thruDate"/>
-                <store-value value-field="workEffortPartyAssignment"/>
-            </if-compare-field>
-        </if-not-empty>
-        <!-- create new record if required -->
-        <if>
-            <condition>
-                <or>
-                    <if-empty field="workEffortPartyAssignments"/>
-                    <and>
-                        <not><if-empty field="workEffortPartyAssignments"/></not>
-                        <if-compare-field field="updProjectRole.newPartyId" operator="not-equals" to-field="workEffortPartyAssignment.partyId"/>
-                    </and>
-                </or>
-            </condition>
-            <then>
-                <make-value entity-name="WorkEffortPartyAssignment" value-field="workEffortPartyAssignment"/>
-                <set field="workEffortPartyAssignment.workEffortId" from-field="parameters.workEffortId"/>
-                <set field="workEffortPartyAssignment.partyId" from-field="updProjectRole.newPartyId"/>
-                <set field="workEffortPartyAssignment.roleTypeId" from-field="updProjectRole.roleTypeId"/>
-                <now-timestamp field="workEffortPartyAssignment.fromDate"/>
-                <create-value value-field="workEffortPartyAssignment"/>
-            </then>
-        </if>
-    </simple-method>
-
-    <simple-method method-name="createProjectTask" short-description="Create a project task and optionally assign">
-        <!-- create task -->
-        <if-empty field="parameters.statusId">
-            <set field="parameters.statusId" value="PTS_CREATED"/>
-        </if-empty>
-        <call-simple-method method-name="createWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-        <set field="parameters.workEffortId" from-field="newEntity.workEffortId"/>
-        <!-- optionally assign to party -->
-        <if-not-empty field="parameters.partyId">
-            <call-simple-method method-name="assignPartyToWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-        </if-not-empty>
-        <!-- optionally enter estimated time and required skill -->
-        <if-not-empty field="parameters.estimatedHours">
-            <set field="parameters.estimatedDuration" from-field="parameters.estimatedHours" type="Double"/>
-            <if-empty field="parameters.skillTypeId">
-                <set field="parameters.skillTypeId" value="_NA_"/>
-            </if-empty>
-            <set-service-fields service-name="createWorkEffortSkillStandard" map="parameters" to-map="createWorkEffortSkillStandard"/>
-            <call-service service-name="createWorkEffortSkillStandard" in-map-name="createWorkEffortSkillStandard"/>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="updateTaskAndRelatedInfo" short-description="Update the task and when info is provided update the related information too">
-        <set-service-fields service-name="updateWorkEffort" map="parameters" to-map="updateWorkeffort"/>
-        <call-service service-name="updateWorkEffort" in-map-name="updateWorkeffort"/>
-        <if-not-empty field="parameters.estimatedDuration">
-            <set-service-fields service-name="updateWorkEffortSkillStandard" map="parameters" to-map="updateWorkEffortSkillStandard"/>
-            <if-empty field="parameters.skillTypeId">
-                <entity-and entity-name="WorkEffortSkillStandard" list="workEffortSkillStandards">
-                    <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-                </entity-and>
-                <if-not-empty field="workEffortSkillStandards">
-                    <first-from-list list="workEffortSkillStandards" entry="workEffortSkillStandard"/>
-                    <set field="updateWorkEffortSkillStandard.skillTypeId" from-field="workEffortSkillStandard.skillTypeId"/>
-                    <else>
-                        <set field="updateWorkEffortSkillStandard.skillTypeId" value="_NA_"/>
-                    </else>
-                </if-not-empty>
-            </if-empty>
-            <entity-one entity-name="WorkEffortSkillStandard" value-field="workEffortSkillStandard">
-                <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-                <field-map field-name="skillTypeId" from-field="updateWorkEffortSkillStandard.skillTypeId"/>
-            </entity-one>
-            <if-not-empty field="workEffortSkillStandard">
-                <call-service service-name="updateWorkEffortSkillStandard" in-map-name="updateWorkEffortSkillStandard"/>
-                <else>
-                    <call-service service-name="createWorkEffortSkillStandard" in-map-name="updateWorkEffortSkillStandard"/>
-                </else>
-            </if-not-empty>
-        </if-not-empty>
-        <!-- if required can update more task related info here -->
-    </simple-method>
-    
-    <simple-method method-name="updateTaskAssigment"
-        short-description="Update task to resource assignment, if required create a new one by re-assigment">
-        <field-to-result field="parameters.workEffortId" result-name="workEffortId"/>
-        <if>
-            <!-- check if a change in partyId Or roletypeId: need to delete and create new -->
-            <condition>
-                <or>
-                    <and>
-                        <not><if-empty field="parameters.newPartyId"/></not>
-                        <if-compare-field field="parameters.partyId" to-field="parameters.newPartyId" operator="not-equals"/>
-                    </and>
-                    <and>
-                        <not><if-empty field="parameters.newRoleTypeId"/></not>
-                        <if-compare-field field="parameters.roleTypeId" to-field="parameters.newRoleTypeId" operator="not-equals"/>
-                    </and>
-                </or>
-            </condition>
-            <then>
-                <!-- roleType and/or partyId changed: end old and create new assign-->
-                <entity-one entity-name="WorkEffortPartyAssignment" value-field="workEffortPartyAssignment"/>
-                <set field="workEffortPartyAssignment.delegateReasonEnumId" from-field="parameters.delegateReasonEnumId"/>
-                <set field="workEffortPartyAssignment.comments" from-field="parameters.comments"/>
-                <set field="workEffortPartyAssignment.assignedByUserLoginId" from-field="userLogin.userLoginId"/>
-                <now-timestamp field="workEffortPartyAssignment.thruDate"/>
-                <store-value value-field="workEffortPartyAssignment"/>
-                <!-- create a new one -->
-                <make-value entity-name="WorkEffortPartyAssignment" value-field="newAssign"/>
-                <set field="newAssign.workEffortId" from-field="parameters.workEffortId"/>
-                <set field="newAssign.partyId" from-field="parameters.newPartyId"/>
-                <set field="newAssign.roleTypeId" from-field="parameters.newRoleTypeId"/>
-                <set field="newAssign.assignedByUserLoginId" from-field="userLogin.userLoginId"/>
-                <now-timestamp field="newAssign.fromDate"/>
-                <set field="newAssign.statusId" value="PAS_ASSIGNED"/>
-                <create-value value-field="newAssign"/>
-            </then>
-            <else>
-                <set field="fromDate" from-field="parameters.fromDate" type="Timestamp"/>
-                <entity-one entity-name="WorkEffortPartyAssignment" value-field="assignment">
-                    <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-                    <field-map field-name="partyId" from-field="parameters.partyId"/>
-                    <field-map field-name="roleTypeId" from-field="parameters.roleTypeId"/>
-                    <field-map field-name="fromDate" from-field="fromDate"/>
-                </entity-one>
-                <if-not-empty field="assignment">
-                    <!-- status changed or assignment ended -->
-                    <if-compare field="parameters.statusId" value="PAS_ENDED" operator="equals">
-                        <!-- special case to indicate end of assignment -->
-                        <now-timestamp field="assignment.thruDate"/>
-                        <clear-field field="parameters.statusId"/>
-                    </if-compare>
-                    <set-nonpk-fields map="parameters" value-field="assignment"/>
-                    <store-value value-field="assignment"/>
-                    <if-compare field="assignment.statusId" value="PAS_COMPLETED" operator="equals">
-                        <call-simple-method method-name="updateTaskStatusToComplete"/>
-                    </if-compare>
-                    <else>
-                        <!-- new assignment -->
-                        <call-simple-method method-name="assignPartyToWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-                    </else>
-                </if-not-empty>
-            </else>
-        </if>
-    </simple-method>
-
-    <simple-method method-name="updateTaskStatusToComplete"
-        short-description="Check partyassignments on a task, if all completes set task status to completed and set actual completiondate to now">
-        <entity-and entity-name="WorkEffortPartyAssignment" list="assignments" filter-by-date="true">
-            <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-        </entity-and>
-        <!-- check if all open assignments were completed -->
-        <if-not-empty field="assignments">
-            <iterate list="assignments" entry="assignment">
-                <if-compare field="assignment.statusId" value="PAS_COMPLETED" operator="not-equals">
-                    <set field="status" value="notcomplete"/>
-                </if-compare>
-            </iterate>
-        </if-not-empty>
-        <if-empty field="status">
-            <now-timestamp field="parameters.actualCompletionDate"/>
-            <set field="parameters.currentStatusId" value="PTS_COMPLETED"/>
-            <call-simple-method method-name="updateWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-            <!-- check for related customer request, set these too to completed -->
-            <entity-and entity-name="CustRequestWorkEffort" list="custRequests">
-                <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-            </entity-and>
-            <if-not-empty field="custRequests">
-                <iterate list="custRequests" entry="custReq">
-                    <set field="updStat.custRequestId" from-field="custReq.custRequestId"/>
-                    <set field="updStat.statusId" value="CRQ_COMPLETED"/>
-                    <call-service service-name="updateCustRequest" in-map-name="updStat"/>
-                </iterate>
-            </if-not-empty>
-        </if-empty>
-    </simple-method>
-
-    <simple-method method-name="scheduleProject" short-description="Project Scheduler sets the planningdates according task requirements and available resources">
-        <!--
-                theory behind the program
-                - - - - - - - - - - - - -
-                (program under development)
-                Assumptions for tasks and resources
-                1. a workday has 8 hours.
-                2. a workweek has 40 hours and 5 days.
-                3. The order of the execution of the tasks is set by the workeffortassociation.
-                4. The default start of the project is today
-                5. default length of a task is 3 day if not planned hours entered
-
-                The steps of the program are:
-                1. read all tasks  and check if there are predesessors, when not set he estimated dates
-                    for critical path processing:
-                    * ES - Earliest Start time
-                    * EF - Earliest Finish time
-                    * LS - Latest Start time
-                    * LF - Latest Finish time
-
-                    EF = LF task is on the critical path
-
-                2. call a recursive java function to set all the dependant tasks.
-        -->
-        <field-to-result field="parameters.projectId" result-name="projectId"/>
-
-        <!-- find a starting point being either the estimated start date of a project or the earliest actual start date. -->
-        <entity-condition entity-name="ProjectAndPhaseAndTask" list="tasks">
-            <condition-expr field-name="actualStartDate" operator="not-equals" from-field="null"/>
-            <order-by field-name="-actualStartDate"/>
-        </entity-condition>
-
-        <if-not-empty field="tasks">
-            <!-- remove all estimated dates -->
-            <iterate list="tasks" entry="task">
-                <clear-field field="task.estimatedStartDate"/>
-                <clear-field field="task.estimatedCompletionDate"/>
-            </iterate>
-
-            <first-from-list list="tasks" entry="task"/>
-            <set field="startDate" from-field="task.actualStartDate"/>
-            <set field="taskId" from-field="task.workEffortId"/>
-            <else>
-                <now-timestamp field="generalStartDate"/>
-            </else>
-        </if-not-empty>
-        <while>
-            <condition>
-                <if-empty field="generalStartDate"/>
-            </condition>
-            <then>
-                <entity-and entity-name="WorkEffortAssoc" list="assocs">
-                    <field-map field-name="workEffortIdTo" from-field="taskId"/>
-                </entity-and>
-                <if-not-empty field="assocs">
-                    <iterate list="assocs" entry="assoc">
-                        <clear-field field="hours"/>
-                        <set field="getTask.taskId" from-field="assoc.workEffortIdFrom"/>
-                        <call-service service-name="getProjectTask" in-map-name="getTask">
-                            <result-to-field result-name="estimatedHours"/>
-                            <result-to-field result-name="actualHours"/>
-                        </call-service>
-                        <if-not-empty field="estimatedHours">
-                            <if-not-empty field="actualHours">
-                                <if-compare-field field="estimatedHours" operator="greater" to-field="actualHours">
-                                    <set field="hours" from-field="estimatedHours" type="Double"/>
-                                    <else>
-                                        <set field="hours" from-field="actualHours" type="Double"/>
-                                    </else>
-                                </if-compare-field>
-                                <else>
-                                    <set field="hours" from-field="estimatedHours" type="Double"/>
-                                </else>
-                            </if-not-empty>
-                        </if-not-empty>
-                        <if-not-empty field="actualHours">
-                            <set field="hours" from-field="actualHours" type="Double"/>
-                        </if-not-empty>
-                        <if-empty field="hours">
-                            <set field="hours" value="16" type="Double"/>
-                        </if-empty>
-                        <if-empty field="highestHours">
-                            <set field="highestHours" from-field="hours" type="Double"/>
-                            <set field="preDesessorId" from-field="assoc.workEffortIdFrom"/>
-                            <else>
-                                <if-compare-field field="highestHours" operator="less" to-field="hours">
-                                    <set field="highestHours" from-field="hours" type="Double"/>
-                                    <set field="preDesessorId" from-field="assoc.workEffortIdFrom"/>
-                                </if-compare-field>
-                            </else>
-                        </if-empty>
-                    </iterate>
-                    <set field="taskId" from-field="preDesessorId"/>
-                    <calculate field="taskDays">
-                        <calcop operator="divide" field="higestHours"/>
-                        <number value="8"/>
-                    </calculate>
-                    <calculate field="taskDays">
-                        <calcop operator="multiply" field="taskDays"/>
-                        <number value="-1"/>
-                    </calculate>
-                    <call-class-method class-name="org.apache.ofbiz.base.util.UtilDateTime" method-name="addDaysToTimestamp" ret-field="startDate">
-                        <field field="startDate"/>
-                        <field field="taskDays"/>
-                    </call-class-method>
-                    <else>
-                        <entity-one entity-name="WorkEffort" value-field="workEffort">
-                            <field-map field-name="workEffortId" from-field="taskId"/>
-                        </entity-one>
-                        <if-not-empty field="workEffortId.parentWorkEffortId">
-                            <set field="taskId" from-field="workEffortId.parentWorkEffortId"/>
-                            <else>
-                                <set field="generalStartDate" from-field="startDate"/>
-                            </else>
-                        </if-not-empty>
-                    </else>
-                </if-not-empty>
-            </then>
-        </while>
-
-        <!-- create the tasklist -->
-        <entity-one entity-name="WorkEffort" value-field="project">
-            <field-map field-name="workEffortId" from-field="parameters.projectId"/>
-        </entity-one>
-        <get-related value-field="project" relation-name="ChildWorkEffort" list="phases"/>
-        <if-not-empty field="phases">
-            <iterate list="phases" entry="phase">
-                <get-related value-field="phase" relation-name="ChildWorkEffort" list="tasks"/>
-                <if-not-empty field="tasks">
-                    <iterate list="tasks" entry="task">
-                        <get-related value-field="task" relation-name="ToWorkEffortAssoc" list="t.prevTasks"/>
-                        <if-empty field="t.prevTasks">
-                            <!-- no predecessors so i can set the dates -->
-                            <now-timestamp field="upd.estimatedStartDate"/>
-                            <now-timestamp field="currentDate"/>
-                            <call-class-method class-name="org.apache.ofbiz.project.Various" method-name="calculateCompletionDate" ret-field="upd.estimatedCompletionDate">
-                                <field field="task" type="org.apache.ofbiz.entity.GenericValue"/>
-                                <field field="currentDate" type="java.sql.Timestamp"/>
-                            </call-class-method>
-                            <set field="upd.workEffortId" from-field="task.workEffortId"/>
-                            <call-service service-name="updateWorkEffort" in-map-name="upd"/>
-                            <entity-one entity-name="WorkEffort" value-field="newTask">
-                                <field-map field-name="workEffortId" from-field="task.workEffortId"/>
-                            </entity-one>
-                            <call-class-method class-name="org.apache.ofbiz.project.Various" method-name="setDatesFollowingTasks">
-                                <field field="newTask" type="org.apache.ofbiz.entity.GenericValue"/>
-                            </call-class-method>
-                        </if-empty>
-                    </iterate>
-                </if-not-empty>
-            </iterate>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="updateTimeEntryByWorkeffort" short-description="Update workeffort by workeffortId and timesheetId ">
-        <field-to-result field="parameters.timesheetId" result-name="timesheetId"/>
-        <if-empty field="parameters.workEffortId">
-            <return/>
-        </if-empty>
-        <if-compare field="parameters.workEffortId" operator="equals" value="Totals">
-            <return/>
-        </if-compare>
-        <entity-one entity-name="Timesheet" value-field="timesheet"/>
-
-        <!-- check if party assigned to task, when not add with roletype of project, if assigned check status -->
-        <entity-and entity-name="WorkEffortPartyAssignment" list="assigns" filter-by-date="true">
-            <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-            <field-map field-name="partyId" from-field="timesheet.partyId"/>
-        </entity-and>
-        <if-empty field="assigns">
-            <set field="getpr.taskId" from-field="parameters.workEffortId"/>
-            <call-service service-name="getProjectIdAndNameFromTask" in-map-name="getpr">
-                <result-to-field result-name="projectId"/>
-            </call-service>
-            <entity-and entity-name="WorkEffortPartyAssignment" list="projectAssigns" filter-by-date="true">
-                <field-map field-name="workEffortId" from-field="projectId"/>
-                <field-map field-name="partyId" from-field="timesheet.partyId"/>
-            </entity-and>
-            <first-from-list list="projectAssigns" entry="projectAssign"/>
-            <set field="parameters.partyId" from-field="timesheet.partyId"/>
-            <set field="parameters.roleTypeId" from-field="projectAssign.roleTypeId"/>
-            <set field="parameters.statusId" value="PAS_ASSIGNED"/>
-            <call-simple-method method-name="assignPartyToWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-        </if-empty>
-        <check-errors/>
-
-        <!-- check if the actual start date is set, when not set it to todays date -->
-        <if-empty field="project.actualStartDate">
-            <entity-one entity-name="WorkEffort" value-field="workEffort"/>
-            <now-timestamp field="workEffort.actualStartDate"/>
-            <store-value value-field="workEffort"/>
-        </if-empty>
-
-        <get-related value-field="timesheet" relation-name="TimeEntry" list="timeEntries"/>
-
-        <!-- update existing entries -->
-        <set field="hours" value="0" type="Double"/>
-        <if-not-empty field="timeEntries">
-            <iterate list="timeEntries" entry="timeEntry">
-                <if-compare-field field="timeEntry.workEffortId" to-field="parameters.workEffortId" operator="equals">
-                    <if-compare-field field="timeEntry.rateTypeId" to-field="parameters.rateTypeId" operator="equals">
-                        <if-not-empty field="timeEntry.hours">
-                            <remove-value value-field="timeEntry"/>
-                            <else>
-                                <!-- translate the date into the daynumber -->
-                                <call-class-method class-name="org.apache.ofbiz.base.util.UtilDateTime" method-name="getIntervalInDays" ret-field="dayNumber">
-                                    <field field="timesheet.fromDate" type="java.sql.Timestamp"/>
-                                    <field field="timeEntry.fromDate" type="java.sql.Timestamp"/>
-                                </call-class-method>
-                                <!-- get the related field -->
-                                <if-not-empty field="parameters.hoursDay${dayNumber}">
-                                    <set field="hours" from-field="parameters.hoursDay${dayNumber}" type="Double"/>
-                                    <else>
-                                        <set field="hours" value="0" type="Double"/>
-                                    </else>
-                                </if-not-empty>
-                                <call-simple-method method-name="updateTimeEntry"/>
-                                <set field="parameters.hoursDay${dayNumber}" value="-999999" type="Double"/>
-                            </else>
-                        </if-not-empty>
-                    </if-compare-field>
-                </if-compare-field>
-            </iterate>
-        </if-not-empty>
-
-        <!-- process not yet done fields -->
-        <loop count="7" field="dayNr">
-            <if-not-empty field="parameters.hoursDay${dayNr}">
-                <if-compare field="parameters.hoursDay${dayNr}" value="-999999" operator="not-equals">
-                    <set field="hours" from-field="parameters.hoursDay${dayNr}" type="Double"/>
-                    <call-class-method class-name="org.apache.ofbiz.base.util.UtilDateTime" method-name="addDaysToTimestamp" ret-field="fromDate">
-                        <field field="timesheet.fromDate" type="java.sql.Timestamp"/>
-                        <field field="dayNr" type="int"/>
-                    </call-class-method>
-                    <call-simple-method method-name="updateTimeEntry"/>
-                </if-compare>
-            </if-not-empty>
-        </loop>
-
-        <!-- update the assignment status if required -->
-        <if-compare field="parameters.checkComplete" value="Y" operator="equals">
-            <entity-and entity-name="WorkEffortPartyAssignment" list="assigns" filter-by-date="true">
-                <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-                <field-map field-name="partyId" from-field="timesheet.partyId"/>
-            </entity-and>
-            <first-from-list list="assigns" entry="alreadyAssign"/>
-            <if-compare field="alreadyAssign.statusId" value="PAS_COMPLETED" operator="not-equals">
-                <set field="upStat.partyId" from-field="timesheet.partyId"/>
-                <set field="upStat.statusId" value="PAS_COMPLETED"/>
-                <set field="upStat.roleTypeId" from-field="alreadyAssign.roleTypeId"/>
-                <set field="upStat.workEffortId" from-field="parameters.workEffortId"/>
-                <set field="upStat.fromDate" from-field="alreadyAssign.fromDate"/>
-                <call-service service-name="updateTaskAssigment" in-map-name="upStat"/>
-            </if-compare>
-        </if-compare>
-    </simple-method>
-
-    <simple-method method-name="getProjectIdAndNameFromTask" short-description="Get the projectId when a phase or task is provided." login-required="true">
-        <if-empty field="parameters.taskId">
-            <if-empty field="parameters.phaseId">
-                <return/>
-            </if-empty>
-        </if-empty>
-        <if-not-empty field="parameters.taskId"><!-- taskId is provided -->
-            <entity-one entity-name="WorkEffort" value-field="task">
-                <field-map field-name="workEffortId" from-field="parameters.taskId"/>
-            </entity-one>
-            <if-not-empty field="task">
-                <get-related-one value-field="task" relation-name="ParentWorkEffort" to-value-field="phase"/>
-                <else>
-                    <return/>
-                </else>
-            </if-not-empty>
-            <else><!-- phaseId is provided -->
-                <entity-one entity-name="WorkEffort" value-field="phase">
-                    <field-map field-name="workEffortId" from-field="parameters.phaseId"/>
-                </entity-one>
-            </else>
-        </if-not-empty>
-        <!-- get project info -->
-        <if-not-empty field="phase">
-            <get-related-one value-field="phase" relation-name="ParentWorkEffort" to-value-field="project"/>
-            <field-to-result field="project.workEffortId" result-name="projectId"/>
-            <field-to-result field="project.workEffortName" result-name="projectName"/>
-            <field-to-result field="phase.workEffortId" result-name="phaseId"/>
-            <field-to-result field="phase.workEffortName" result-name="phaseName"/>
-            <field-to-result field="task.workEffortId" result-name="taskId"/>
-            <field-to-result field="task.workEffortName" result-name="taskName"/>
-            <set field="taskWbsId" value="${project.workEffortId}.${phase.sequenceNum}.${task.sequenceNum}"/>
-            <field-to-result field="taskWbsId"/>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="copyProjectToTemplate" short-description="copy a project to a workeffortType starting with 'template'">
-        <set field="toTemplate" value="dummy"/>
-        <call-simple-method method-name="copyProject"/>
-    </simple-method>
-
-    <simple-method method-name="copyProject" short-description="copy a project with related phases and tasks however no actual data">
-        <entity-one entity-name="WorkEffort" value-field="project">
-            <field-map field-name="workEffortId" from-field="parameters.projectId"/>
-        </entity-one>
-
-        <if-empty field="project">
-            <field-to-result field="parameters.projectId" result-name="projectId"/>
-            <add-error error-list-name="error_list">
-                <fail-property resource="ProjectMgrUiLabels" property="ProjectMgrErrorProjectNotFound"/>
-            </add-error>
-        </if-empty>
-        <if-empty field="parameters.workEffortName">
-            <set field="parameters.workEffortName" from-field="project.workEffortName"/>
-        </if-empty>
-        <if-empty field="parameters.description">
-            <set field="parameters.description" from-field="project.description"/>
-        </if-empty>
-        <if-not-empty field="toTemplate">
-            <set field="parameters.workEffortTypeId" value="PROJECT_TEMPLATE"/>
-            <else>
-                <set field="parameters.workEffortTypeId" value="PROJECT"/>
-            </else>
-        </if-not-empty>
-        <set field="parameters.currentStatusId" value="PRJ_ACTIVE"/>
-        <clear-field field="parameters.workEffortId"/>
-        <call-simple-method method-name="createWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-        <set field="newProjectId" from-field="newEntity.workEffortId"/>
-
-        <!-- copy assigned parties -->
-        <get-related value-field="project" relation-name="WorkEffortPartyAssignment" list="partiesAll"/>
-        <filter-list-by-date list="partiesAll" to-list="parties"/>
-        <if-not-empty field="parties">
-            <iterate list="parties" entry="party">
-                <set field="parameters.workEffortId" from-field="newProjectId"/>
-                <set field="parameters.partyId" from-field="party.partyId"/>
-                <set field="parameters.roleTypeId" from-field="party.roleTypeId"/>
-                <set field="parameters.statusId" value="PAS_ASSIGNED"/>
-                <call-simple-method method-name="assignPartyToWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-            </iterate>
-        </if-not-empty>
-
-        <get-related value-field="project" relation-name="ChildWorkEffort" list="phases"/>
-        <if-not-empty field="phases">
-            <iterate list="phases" entry="phase">
-                <clear-field field="newPhase"/>
-                <if-not-empty field="toTemplate">
-                    <set field="parameters.workEffortTypeId" value="PHASE_TEMPLATE"/>
-                    <else>
-                        <set field="parameters.workEffortTypeId" value="PHASE"/>
-                    </else>
-                </if-not-empty>
-                <set field="parameters.workEffortName" from-field="phase.workEffortName"/>
-                <set field="parameters.workEffortParentId" from-field="newProjectId"/>
-                <set field="parameters.currentStatusId" value="_NA_"/>
-                <clear-field field="parameters.workEffortId"/>
-                <call-simple-method method-name="createWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-                <set field="newPhaseId" from-field="newEntity.workEffortId"/>
-
-                <get-related value-field="phase" relation-name="ChildWorkEffort" list="tasks"/>
-                <if-not-empty field="tasks">
-                    <iterate list="tasks" entry="task">
-                        <clear-field field="newTask"/>
-                        <if-not-empty field="toTemplate">
-                            <set field="newTask.workEffortTypeId" value="TASK_TEMPLATE"/>
-                            <else>
-                                <set field="newTask.workEffortTypeId" value="TASK"/>
-                            </else>
-                        </if-not-empty>
-                        <set field="parameters.workEffortName" from-field="task.workEffortName"/>
-                        <set field="parameters.priority" from-field="task.priority"/>
-                        <set field="parameters.workEffortParentId" from-field="newPhaseId"/>
-                        <set field="parameters.currentStatusId" value="PTS_CREATED"/>
-                        <clear-field field="parameters.workEffortId"/>
-                        <call-simple-method method-name="createWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-                    </iterate>
-                </if-not-empty>
-            </iterate>
-        </if-not-empty>
-        <field-to-result field="newProjectId" result-name="projectId"/>
-    </simple-method>
-
-    <simple-method method-name="getProject" short-description="get Project information" login-required="true">
-        <if-empty field="parameters.projectId">
-            <return/>
-        </if-empty>
-        <if-not-empty field="parameters.partyId">
-            <set field="parameters.hoursPartyId" from-field="parameters.partyId"/>
-        </if-not-empty>
-        <entity-one entity-name="WorkEffort" value-field="project">
-            <field-map field-name="workEffortId" from-field="parameters.projectId"/>
-        </entity-one>
-        <set field="highInfo.projectId" from-field="project.workEffortId"/>
-        <set field="highInfo.projectName" from-field="project.workEffortName"/>
-        <set field="highInfo.projectDescription" from-field="project.description"/>
-        <set field="highInfo.estimatedStartDate" from-field="project.estimatedStartDate"/>
-        <set field="highInfo.estimatedCompletionDate" from-field="project.estimatedCompletionDate"/>
-        <set field="highInfo.actualStartDate" from-field="project.actualStartDate"/>
-        <set field="highInfo.actualCompletionDate" from-field="project.actualCompletionDate"/>
-        <set field="highInfo.scopeEnumId" from-field="project.scopeEnumId"/>
-        <set field="highInfo.createdStamp" from-field="project.createdStamp"/>
-        <entity-one entity-name="StatusItem" value-field="highSeq">
-            <field-map field-name="statusId" from-field="project.currentStatusId"/>
-        </entity-one>
-        <set field="highInfo.createdDate" from-field="project.createdDate"/>
-        <set field="highInfo.parentProjectId" from-field="project.workEffortParentId"/>
-        <!-- loop through the related phases and tasks -->
-        <call-simple-method method-name="combineInfo"/>
-
-        <!-- get e-mail address -->
-        <entity-and entity-name="WorkEffortContactMechView" list="emailAddresses">
-            <field-map field-name="workEffortId" from-field="highInfo.projectId"/>
-            <field-map field-name="contactMechTypeId" value="EMAIL_ADDRESS"/>
-        </entity-and>
-        <filter-list-by-date list="emailAddresses"/>
-        <if-not-empty field="emailAddresses">
-            <first-from-list list="emailAddresses" entry="emailAddress"/>
-            <set field="highInfo.emailAddress" from-field="emailAddress.infoString"/>
-        </if-not-empty>
-
-        <!-- closed overrules everything -->
-        <if-compare field="project.currentStatusId" value="PRJ_CLOSED" operator="equals">
-            <set field="highInfo.currentStatusId" value="PRJ_CLOSED"/>
-        </if-compare>
-
-        <call-simple-method method-name="createDates"/>
-
-        <!-- results -->
-        <field-to-result field="highInfo" result-name="projectInfo"/>
-        <field-to-result field="parameters.projectId" result-name="projectId"/>
-    </simple-method>
-
-    <simple-method method-name="getProjectPhaseList" short-description="get Project Phase information" login-required="true">
-        <if-empty field="parameters.projectId">
-            <return/>
-        </if-empty>
-        <entity-and entity-name="WorkEffort" list="phases">
-            <field-map field-name="workEffortTypeId" value="PHASE"/>
-            <field-map field-name="workEffortParentId" from-field="parameters.projectId"/>
-            <order-by field-name="sequenceNum"/>
-            <order-by field-name="workEffortName"/>
-        </entity-and>
-        <if-not-empty field="phases">
-        <iterate list="phases" entry="phase">
-            <!-- get the phase seq id -->
-            <entity-one entity-name="StatusItem" value-field="highSeq">
-                <field-map field-name="statusId" from-field="phase.currentStatusId"/>
-            </entity-one>
-            <clear-field field="highInfo"/>
-            <set field="highInfo.sequenceId" from-field="highSeq.sequenceId"/>
-            <set field="highInfo.phaseId" from-field="phase.workEffortId"/>
-            <set field="highInfo.phaseSeqNum" from-field="phase.sequenceNum"/>
-            <set field="highInfo.phaseName" from-field="phase.workEffortName"/>
-            <set field="highInfo.phaseDescription" from-field="phase.description"/>
-            <set field="highInfo.scopeEnumId" from-field="phase.scopeEnumId"/>
-
-            <!-- loop through the related tasks and combine information -->
-            <call-simple-method method-name="combineInfo"/>
-
-            <!-- merge estimated and actual dates -->
-            <call-simple-method method-name="createDates"/>
-
-            <field-to-list field="highInfo" list="phaseList"/>
-        </iterate>
-        </if-not-empty>
-
-        <!-- results -->
-        <field-to-result field="phaseList"/>
-        <field-to-result field="parameters.projectId" result-name="projectId"/>
-    </simple-method>
-
-    <simple-method method-name="getProjectTaskList" short-description="get Project Phase/task information" login-required="true">
-        <if-empty field="parameters.projectId">
-            <return/>
-        </if-empty>
-        <entity-and entity-name="ProjectAndPhaseAndTask" list="tasks">
-            <field-map field-name="projectId" from-field="parameters.projectId"/>
-            <order-by field-name="phaseSeqNum"/>
-            <order-by field-name="phaseName"/>
-            <order-by field-name="sequenceNum"/>
-            <order-by field-name="workEffortName"/>
-        </entity-and>
-        <if-not-empty field="tasks">
-            <iterate list="tasks" entry="lowInfo">
-                <set field="highInfo.phaseName" from-field="lowInfo.phaseName"/>
-                <set field="highInfo.phaseSeqNum" from-field="lowInfo.phaseSeqNum"/>
-                <set field="highInfo.taskId" from-field="lowInfo.workEffortId"/>
-                <call-simple-method method-name="combineInfo"/>
-                <clear-field field="highInfo.sequenceId"/>
-                <call-simple-method method-name="createDates"/>
-                <set field="highInfo.workEffortId" from-field="lowInfo.workEffortId"/>
-                <set field="highInfo.workEffortName" from-field="lowInfo.workEffortName"/>
-                <set field="highInfo.sequenceNum" from-field="lowInfo.sequenceNum"/>
-                <field-to-list field="highInfo" list="taskList"/>
-                <clear-field field="highInfo"/>
-            </iterate>
-        </if-not-empty>
-        <!-- results -->
-        <field-to-result field="taskList"/>
-        <field-to-result field="parameters.projectId" result-name="projectId"/>
-    </simple-method>
-
-    <simple-method method-name="getProjectTask" short-description="get Project task information" login-required="true">
-        <entity-one entity-name="WorkEffort" value-field="lowInfo">
-            <field-map field-name="workEffortId" from-field="parameters.taskId"/>
-        </entity-one>
-        <set field="highInfo.taskId" from-field="lowInfo.workEffortId"/>
-        <set field="highInfo.taskSeqNum" from-field="lowInfo.sequenceNum"/>
-        <set field="highInfo.taskName" from-field="lowInfo.workEffortName"/>
-        <set field="highInfo.taskDescription" from-field="lowInfo.description"/>
-        <set field="highInfo.scopeEnumId" from-field="lowInfo.scopeEnumId"/>
-        <set field="highInfo.workEffortParentId" from-field="lowInfo.workEffortParentId"/>
-        <call-simple-method method-name="combineInfo"/>
-        <field-to-result field="highInfo" result-name="taskInfo"/>
-    </simple-method>
-
-    <simple-method method-name="getProjectsByParties" short-description="get Project information by party member" login-required="true">
-        <entity-condition entity-name="ProjectAndPhaseAndTaskParty" list="tasks">
-            <condition-list combine="and">
-                <condition-expr field-name="projectId" operator="equals" from-field="parameters.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="partyId" operator="equals" from-field="parameters.partyId" ignore-if-empty="true"/>
-                <condition-expr field-name="partyId" operator="not-equals" from-field="null"/>
-            </condition-list>
-            <order-by field-name="projectId"/>
-            <order-by field-name="partyId"/>
-        </entity-condition>
-        <if-not-empty field="tasks">
-            <iterate list="tasks" entry="task">
-                <if>
-                    <condition>
-                        <and>
-                            <not><if-empty field="projectParty"/></not>
-                            <if-compare-field field="task.partyId" to-field="projectParty.partyId" operator="not-equals"/>
-                        </and>
-                    </condition>
-                    <then>
-                        <field-to-list field="projectParty" list="projectParties"/>
-                        <clear-field field="projectParty"/>
-                        <clear-field field="highInfo"/>
-                    </then>
-                </if>
-
-                <if-empty field="projectParty">
-                    <set field="projectParty.partyId" from-field="task.partyId"/>
-                    <entity-one entity-name="PartyNameView" value-field="partyNameView">
-                        <field-map field-name="partyId" from-field="task.partyId"/>
-                    </entity-one>
-                    <if-not-empty field="partyNameView">
-                        <set field="projectParty.partyName" value="${partyNameView.lastName},${partyNameView.firstName}${partyNameView.groupName}"/>
-                    </if-not-empty>
-                    <set field="projectParty.roleTypeId" from-field="task.roleTypeId"/>
-                    <set field="projectParty.fromDate" from-field="task.fromDate"/>
-                    <set field="projectParty.thruDate" from-field="task.thruDate"/>
-                </if-empty>
-
-                <!-- get the planned/actual hours -->
-                <set field="lowInfo" from-field="task"/>
-                <set field="parameters.hoursPartyId" from-field="task.partyId"/>
-                <call-simple-method method-name="getHours" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-                <set field="projectParty.plannedHours" from-field="highInfo.plannedHours" type="Double"/>
-                <set field="projectParty.actualHours" from-field="highInfo.actualHours" type="Double"/>
-            </iterate>
-            <if-not-empty field="projectParty">
-                <field-to-list field="projectParty" list="projectParties"/>
-            </if-not-empty>
-            <if-not-empty field="projectParties">
-                <field-to-result field="projectParties"/>
-            </if-not-empty>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="getTasksByParties" short-description="get task information by party member" login-required="true">
-        <!-- get the list of tasks optionaly selected for a party -->
-        <entity-condition entity-name="WorkEffortPartyAssignment" list="tasks" filter-by-date="true">
-            <condition-list combine="and">
-                <condition-expr field-name="partyId" operator="equals" from-field="parameters.partyId" ignore-if-empty="true"/>
-                <condition-expr field-name="workEffortId" operator="equals" from-field="parameters.workEffortId" ignore-if-empty="true"/>
-            </condition-list>
-            <order-by field-name="workEffortId"/>
-            <order-by field-name="partyId"/>
-        </entity-condition>
-        <if-not-empty field="tasks">
-            <iterate list="tasks" entry="task">
-                <if>
-                    <condition >
-                        <and>
-                            <not>
-                                <if-empty field="taskParty"/>
-                            </not>
-                            <if-compare-field field="task.partyId" to-field="taskParty.partyId" operator="not-equals"/>
-                        </and>
-                    </condition>
-                    <then>
-                        <field-to-list field="taskParty" list="taskParties"/>
-                        <clear-field field="taskParty"/>
-                        <clear-field field="highInfo"/>
-                    </then>
-                </if>
-
-                <if-empty field="taskParty">
-                    <set field="taskParty.partyId" from-field="task.partyId"/>
-                    <entity-one entity-name="PartyNameView" value-field="partyNameView">
-                        <field-map field-name="partyId" from-field="task.partyId"/>
-                    </entity-one>
-                    <if-not-empty field="partyNameView">
-                        <set field="taskParty.partyName" value="${partyNameView.lastName},${partyNameView.firstName}${partyNameView.groupName}"/>
-                    </if-not-empty>
-                    <set field="taskParty.roleTypeId" from-field="task.roleTypeId"/>
-                    <set field="taskParty.statusId" from-field="task.statusId"/>
-                    <set field="taskParty.fromDate" from-field="task.fromDate"/>
-                    <set field="taskParty.thruDate" from-field="task.thruDate"/>
-                </if-empty>
-
-                <!-- get the planned hours -->
-                <set field="lowInfo" from-field="task"/>
-                <set field="parameters.hoursPartyId" from-field="task.partyId"/>
-                <get-related-one value-field="task" relation-name="WorkEffort" to-value-field="lowInfo"/>
-                <call-simple-method method-name="getHours" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-                <set field="taskParty.plannedHours" from-field="highInfo.plannedHours" type="Double"/>
-                <set field="taskParty.actualHours" from-field="highInfo.actualHours" type="Double"/>
-                <set field="taskParty.originalActualHours" from-field="highInfo.originalActualHours" type="Double"/>
-            </iterate>
-            <if-not-empty field="taskParty">
-                <field-to-list field="taskParty" list="taskParties"/>
-            </if-not-empty>
-            <if-not-empty field="taskParties">
-                <field-to-result field="taskParties"/>
-            </if-not-empty>
-        </if-not-empty>
-    </simple-method>
-
-    <!-- Internal functions  -->
-    <simple-method method-name="combineStatusInfo" short-description="combine lower level status">
-        <!-- the status for a project or phase is
-         IN_PROGRESS if at least one task still in progress
-         COMPLETED if all task are either completed or cancelled
-         CREATED if other conditions does not apply
-         For a task the status is
-         IN_PROGRESS if it has at least one resource and at least a time entry
-         ASSIGNED if it has at least one resource but no time entry associated
-         -->
-        <!-- if lowlevel type equals TASK then get create the status first -->
-        <if-compare field="lowInfo.workEffortTypeId" value="TASK" operator="equals">
-            <set field="highInfo.currentStatusId" from-field="lowInfo.currentStatusId"/>
-            <if-compare field="lowInfo.currentStatusId" value="PTS_CREATED" operator="equals">
-                <get-related value-field="lowInfo" relation-name="WorkEffortPartyAssignment" list="assignsAll"/>
-                <filter-list-by-date list="assignsAll" to-list="assigns"/>
-                <if-not-empty field="assigns">
-                    <set field="highInfo.currentStatusId" value="PTS_CREATED_AS"/><!-- task is ASSIGNED -->
-                </if-not-empty>
-                <get-related value-field="lowInfo" relation-name="TimeEntry" list="entries"/>
-                <if-not-empty field="entries">
-                    <set field="highInfo.currentStatusId" value="PTS_CREATED_IP"/><!-- task is IN_PROGRESS -->
-                </if-not-empty>
-            </if-compare>
-            <return/>
-        </if-compare>
-        <entity-count count-field="tasksCount" entity-name="ProjectPhaseTaskAssignmentView">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-            </condition-list>
-        </entity-count>
-        <log level="info" message="related tasks count ====> ${tasksCount}"/>
-        <entity-count count-field="completedTasks" entity-name="ProjectPhaseTaskAssignmentView">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-            </condition-list>
-            <having-condition-list combine="or">
-            <condition-expr field-name="taskStatusId" operator="equals" value="PTS_COMPLETED"/>
-            <condition-expr field-name="taskStatusId" operator="equals" value="PTS_CANCELLED"/>
-        </having-condition-list>
-        </entity-count>
-        <log level="info" message="related completed tasks count ====> ${completedTasks}"/>
-        <entity-count count-field="assignedTasks" entity-name="ProjectPhaseTaskAssignmentView">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-            </condition-list>
-            <having-condition-list combine="and">
-            <condition-expr field-name="entriesCount" value="0"/>
-            <condition-expr field-name="resourceCount" operator="greater-equals" value="1"/>
-            <condition-expr field-name="taskStatusId" operator="not-equals" value="PTS_COMPLETED"/>
-            <condition-expr field-name="taskStatusId" operator="not-equals" value="PTS_CANCELLED"/>
-        </having-condition-list>
-        </entity-count>
-        <log level="info" message="related assigned tasks count ====> ${assignedTasks}"/>
-        <entity-count count-field="inprogressTasks" entity-name="ProjectPhaseTaskAssignmentView">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-            </condition-list>
-            <having-condition-list combine="and">
-                <condition-expr field-name="entriesCount" operator="greater-equals" value="1"/>
-                <condition-expr field-name="resourceCount" operator="greater-equals" value="1"/>
-                <condition-expr field-name="taskStatusId" operator="not-equals" value="PTS_COMPLETED"/>
-                <condition-expr field-name="taskStatusId" operator="not-equals" value="PTS_CANCELLED"/>
-            </having-condition-list>
-        </entity-count>
-        <log level="info" message="related in progress tasks count ====> ${inprogressTasks}"/>
-        <if>
-            <condition>
-                <or>
-                    <if-compare field="inprogressTasks" type="Long" operator="greater" value="0"/>
-                    <if-compare field="assignedTasks" type="Long" operator="greater" value="0"/>
-                </or>
-            </condition>
-            <then>
-                <set field="highInfo.currentStatusId" value="PTS_CREATED_IP"/>
-            </then>
-            <else>
-                <if>
-                    <condition>
-                        <if-compare-field operator="equals" field="completedTasks" to-field="tasksCount"/>
-                    </condition>
-                    <then>
-                        <set field="highInfo.currentStatusId" value="PTS_COMPLETED"/>
-                    </then>
-                    <else>
-                        <set field="highInfo.currentStatusId" value="PTS_CREATED"/>
-                    </else>
-                </if>
-            </else>
-        </if>
-    </simple-method>
-
-    <simple-method method-name="combineDatesAndPlannedHoursInfo" short-description="combine lower level start end dates and planned hours for a project, phase or task">
-        <entity-condition  entity-name="ProjectPhaseTaskSklSumView" list="summaryInfos">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-            </condition-list>
-            <select-field field-name="projectId"/>
-            <select-field field-name="estimatedStartDate"/>
-            <select-field field-name="actualStartDate"/>
-            <select-field field-name="estimatedCompletionDate"/>
-            <select-field field-name="actualCompletionDate"/>
-            <select-field field-name="plannedHours"/>
-            <select-field field-name="priority"/>
-        </entity-condition>
-
-        <!-- Now used TimeEntries to update (or not) actual start and end Date -->
-        <first-from-list list="summaryInfos" entry="summaryInfo"/>
-        <set field="highInfo.estimatedStartDate" from-field="summaryInfo.estimatedStartDate"/>
-        <set field="highInfo.estimatedCompletionDate" from-field="summaryInfo.estimatedCompletionDate"/>
-        <set field="highInfo.actualStartDate" from-field="summaryInfo.actualStartDate"/>
-        <set field="highInfo.actualCompletionDate" from-field="summaryInfo.actualCompletionDate"/>
-        <set field="highInfo.priority" from-field="summaryInfo.priority"/>
-        <set field="highInfo.plannedHours" from-field="summaryInfo.plannedHours"/>
-        <!-- <log level="info" message="projectId=${highInfo.projectId} phaseId=${highInfo.phaseId} taskId=${highInfo.taskId} highInfo.plannedHours=${highInfo.plannedHours}"/>-->
-        <!-- update actual start date by the min date form sub tasks associated TimeEntries 
-        (if before actualStartDate field) -->
-        <entity-condition  entity-name="ProjectPhaseTaskActualEntrySumView" list="summaryEntriesInfos">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-            </condition-list>
-            <select-field field-name="actualEntryStartDate"/>
-        </entity-condition>
-        <first-from-list list="summaryEntriesInfos" entry="timeEntriesInfo"/>
-        <if-not-empty field="timeEntriesInfo">
-          <if-not-empty field="timeEntriesInfo.actualEntryStartDate">
-            <if>
-                <condition>
-                    <or>
-                        <if-empty field="highInfo.actualStartDate"/>
-                        <if-compare-field field="highInfo.actualStartDate" operator="greater" type="Timestamp" to-field="timeEntriesInfo.actualEntryStartDate"/>
-                    </or>
-                </condition>
-                <then>
-                    <set field="highInfo.actualStartDate" from-field="timeEntriesInfo.actualEntryStartDate"/>
-                </then>
-            </if>
-          </if-not-empty>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="combineInfo" short-description="combine lower level status, dates of tasks.">
-        <call-simple-method method-name="combineStatusInfo"/>
-        <call-simple-method method-name="combineDatesAndPlannedHoursInfo"/>
-        <call-simple-method method-name="combineActualHours"/>
-    </simple-method>
-
-    <simple-method method-name="combineActualHours" short-description="combine lower level Actual hours info.">
-        <!-- 
-        -to calculate actual hours : the declared number of hours in time entry should be multiplied by the 
-         max percentage declared in PartyRate if a valid party rate can be found for the party associated to a
-         the timesheet associated to this time entry and has the same rateType as this timeEntry 
-        -actualHoursOriginal is the total of hours in time entries without application of percentage declared in partyRate
-         -->
-        <clear-field field="originalHours"/>
-        <clear-field field="actualHours"/>
-        <clear-field field="originalActualHours"/>
-
-        <!-- I- get timeEntries for which there is no rate  (originalHours)-->
-        <entity-condition entity-name="ProjectPhaseTaskActualNotRatedHoursView" list="notRatedValues">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-                <condition-expr field-name="hoursPartyId" from-field="parameters.hoursPartyId" ignore-if-empty="true"/>
-            </condition-list>
-            <select-field field-name="totalOriginalHours"/>
-        </entity-condition>
-        <first-from-list list="notRatedValues" entry="notRatedValue"/>
-        <set field="originalHours" from-field="notRatedValue.totalOriginalHours" type="Double"/>
-
-        <!-- II- get total for timeEntries having a partyRate that should be applied
-        before applying rate (totalOriginalHours)
-        after applying rate (totalRatedHours)-->
-        <entity-condition entity-name="ProjectPhaseTaskActualRatedHoursView" list="ratedValues">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-                <condition-expr field-name="hoursPartyId" from-field="parameters.hoursPartyId" ignore-if-empty="true"/>
-            </condition-list>
-            <select-field field-name="totalOriginalHours"/>
-            <select-field field-name="totalRatedHours"/>
-        </entity-condition>
-        <first-from-list list="ratedValues" entry="ratedValue"/>
-        <!-- not used ratedValue.totalRatedHours  because not works, reason seem to be totalRatedHours is a calculated field ??? -->
-        <call-object-method method-name="getDouble" obj-field="ratedValue" ret-field="actualHours">
-            <string value="totalRatedHours"/>
-        </call-object-method>
-
-        <if-empty field="actualHours">
-            <set field="actualHours" from-field="originalHours"/>
-            <else>
-                <calculate field="actualHours" type="Double">
-                    <calcop operator="add" field="originalHours">
-                        <calcop operator="get" field="actualHours"/>
-                    </calcop>
-                </calculate>
-            </else>
-        </if-empty>
-
-        <if-empty field="originalHours">
-            <set field="originalActualHours" from-field="ratedValue.totalOriginalHours" type="Double"/>
-            <else>
-                <calculate field="originalActualHours" type="Double">
-                    <calcop operator="add" field="originalHours">
-                        <calcop operator="get" field="ratedValue.totalOriginalHours"/>
-                    </calcop>
-                </calculate>
-            </else>
-        </if-empty>
-
-        <set field="highInfo.originalActualHours" from-field="originalActualHours"/>
-        <set field="highInfo.actualHours" from-field="actualHours"/>
-        <!-- do the same but for non-billed hours -->
-        <!-- first get not rated hours -->
-        <entity-condition entity-name="ProjectPhaseTaskActualNotRatedHoursView" list="notRatedValues">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-                <condition-expr field-name="hoursPartyId" from-field="parameters.hoursPartyId" ignore-if-empty="true"/>
-                <condition-expr field-name="invoiceId" from-field="nullField"/>
-            </condition-list>
-            <select-field field-name="totalOriginalHours"/>
-        </entity-condition>
-        <first-from-list list="notRatedValues" entry="notRatedValue"/>
-        <set field="actualNonBilledHours" from-field="notRatedValue.totalOriginalHours" type="Double"/>
-        <!-- second get non billed for entries having an invoiceId -->
-        <entity-condition entity-name="ProjectPhaseTaskActualRatedHoursView" list="ratedValues">
-            <condition-list>
-                <condition-expr field-name="projectId" from-field="highInfo.projectId" ignore-if-empty="true"/>
-                <condition-expr field-name="phaseId" from-field="highInfo.phaseId" ignore-if-empty="true"/>
-                <condition-expr field-name="taskId" from-field="highInfo.taskId" ignore-if-empty="true"/>
-                <condition-expr field-name="hoursPartyId" from-field="parameters.hoursPartyId" ignore-if-empty="true"/>
-                <condition-expr field-name="invoiceId" from-field="nullField"/>
-            </condition-list>
-            <select-field field-name="totalOriginalHours"/>
-            <select-field field-name="totalRatedHours"/>
-        </entity-condition>
-        <first-from-list list="ratedValues" entry="ratedValue"/>
-        <call-object-method method-name="getDouble" obj-field="ratedValue" ret-field="actualHours">
-            <string value="totalOriginalHours"/>
-        </call-object-method>
-
-        <if-not-empty field="actualNonBilledHours">
-            <calculate field="actualNonBilledHours" type="Double">
-                <calcop operator="get" field="actualNonBilledHours">
-                    <calcop operator="add" field="actualHours"/>
-                </calcop>
-            </calculate>
-            <else>
-                <set field="actualNonBilledHours" from-field="totalOriginalHours" default-value="0" type="Double"/>
-            </else>
-        </if-not-empty>
-        <set field="highInfo.actualNonBilledHours" from-field="actualNonBilledHours" type="Double"/>
-    </simple-method>
-
-    <simple-method method-name="combineInfoOld" short-description="combine lower level status, dates of tasks.">
-        <!-- in/output highInfo infoMap -->
-        <!-- input lowInfo info map -->
-        <!-- set the dates from the lower level tasks -->
-        <if-not-empty field="lowInfo.estimatedStartDate">
-            <if-empty field="highInfo.estimatedStartDate">
-                <set field="highInfo.estimatedStartDate" from-field="lowInfo.estimatedStartDate"/>
-                <else>
-                    <if-compare-field to-field="lowInfo.estimatedStartDate" field="highInfo.estimatedStartDate" operator="greater" type="Timestamp">
-                        <set field="highInfo.estimatedStartDate" from-field="lowInfo.estimatedStartDate"/>
-                    </if-compare-field>
-                </else>
-            </if-empty>
-        </if-not-empty>
-        <if-not-empty field="lowInfo.estimatedCompletionDate">
-            <if-empty field="highInfo.estimatedCompletionDate">
-                <set field="highInfo.estimatedCompletionDate" from-field="lowInfo.estimatedCompletionDate"/>
-                <else>
-                    <if-compare-field to-field="lowInfo.estimatedCompletionDate" field="highInfo.estimatedCompletionDate" operator="less" type="Timestamp">
-                        <set field="highInfo.estimatedCompletionDate" from-field="lowInfo.estimatedCompletionDate"/>
-                    </if-compare-field>
-                </else>
-            </if-empty>
-        </if-not-empty>
-        <if-not-empty field="lowInfo.actualStartDate">
-            <if-empty field="highInfo.actualStartDate">
-                <set field="highInfo.actualStartDate" from-field="lowInfo.actualStartDate"/>
-                <else>
-                    <if-compare-field to-field="lowInfo.actualStartDate" field="highInfo.actualStartDate" operator="greater" type="Timestamp">
-                        <set field="highInfo.actualStartDate" from-field="lowInfo.actualStartDate"/>
-                    </if-compare-field>
-                </else>
-            </if-empty>
-        </if-not-empty>
-        <if-not-empty field="lowInfo.actualCompletionDate">
-            <if-empty field="highInfo.actualCompletionDate">
-                <set field="highInfo.actualCompletionDate" from-field="lowInfo.actualCompletionDate"/>
-                <else>
-                    <if-compare-field to-field="lowInfo.actualCompletionDate" field="highInfo.actualCompletionDate" operator="less" type="Timestamp">
-                        <set field="highInfo.actualCompletionDate" from-field="lowInfo.actualCompletionDate"/>
-                    </if-compare-field>
-                </else>
-            </if-empty>
-        </if-not-empty>
-
-        <!-- combine the priorities -->
-        <if-not-empty field="lowInfo.priority">
-            <if-empty field="highInfo.priority">
-                <set field="highInfo.priority" from-field="lowInfo.priority"/>
-                <else>
-                    <if-compare-field to-field="lowInfo.priority" field="highInfo.priority" operator="greater">
-                        <set field="highInfo.priority" from-field="lowInfo.priority"/>
-                    </if-compare-field>
-                </else>
-            </if-empty>
-        </if-not-empty>
-
-        <!-- if lowlevel type equals TASK then get create the status first -->
-        <if-compare field="lowInfo.workEffortTypeId" value="TASK" operator="equals">
-            <if-compare field="lowInfo.currentStatusId" value="PTS_CREATED" operator="equals">
-                <get-related value-field="lowInfo" relation-name="WorkEffortPartyAssignment" list="assignsAll"/>
-                <filter-list-by-date list="assignsAll" to-list="assigns"/>
-                <if-not-empty field="assigns">
-                    <set field="lowInfo.currentStatusId" value="PTS_CREATED_AS"/><!-- task is assigned -->
-                </if-not-empty>
-                <get-related value-field="lowInfo" relation-name="TimeEntry" list="entries"/>
-                <if-not-empty field="entries">
-                    <set field="lowInfo.currentStatusId" value="PTS_CREATED_IP"/><!-- task is in progress -->
-                </if-not-empty>
-            </if-compare>
-        </if-compare>
-
-        <entity-one entity-name="StatusItem" value-field="status">
-            <field-map field-name="statusId" from-field="lowInfo.currentStatusId"/>
-        </entity-one>
-        <if-empty field="highInfo.sequenceId">
-            <set field="highInfo.sequenceId" from-field="status.sequenceId"/>
-            <else>
-                <if>
-                    <condition>
-                        <and>
-                            <if-compare-field field="highInfo.sequenceId" to-field="status.sequenceId" operator="less"/>
-                            <and>
-                                <if-compare field="highInfo.sequenceId" value="05" operator="not-equals"/>
-                                <if-compare field="status.sequenceId" value="09" operator="not-equals"/>
-                            </and>
-                        </and>
-                    </condition>
-                    <then>
-                        <set field="highInfo.sequenceId" from-field="status.sequenceId"/>
-                    </then>
-                    <else>
-                        <set field="highInfo.sequenceId" value="05"/>
-                    </else>
-                </if>
-            </else>
-        </if-empty>
-    </simple-method>
-
-    <simple-method method-name="createDates" short-description="merge the estimated and actual dates">
-        <!-- input/output is 'highInfo map -->
-        <!-- create dates taking the last known one to save space on the list -->
-        <if-not-empty field="highInfo.actualStartDate">
-            <set field="highInfo.startDate" from-field="highInfo.actualStartDate"/>
-            <else>
-                <set field="highInfo.startDate" from-field="highInfo.estimatedStartDate"/>
-            </else>
-        </if-not-empty>
-        <if-not-empty field="highInfo.actualCompletionDate">
-            <set field="highInfo.completionDate" from-field="highInfo.actualCompletionDate"/>
-            <else>
-                <set field="highInfo.completionDate" from-field="highInfo.estimatedCompletionDate"/>
-            </else>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="updateTimeEntry" short-description="">
-        <if-compare field="hours" value="-1" operator="equals">
-            <return/>
-        </if-compare>
-        <if-not-empty field="timeEntry.timeEntryId">
-            <if-compare field="hours" operator="equals" value="0">
-                <set  field="teDelMap.timeEntryId" from-field="timeEntry.timeEntryId"/>
-                <call-service service-name="deleteTimeEntry" in-map-name="teDelMap"/>
-                <else>
-                    <clear-field field="teUpdMap"/>
-                    <set field="teUpdMap.hours" from-field="hours" type="Double"/>
-                    <set field="teUpdMap.timeEntryId" from-field="timeEntry.timeEntryId"/>
-                    <set field="teUpdMap.rateTypeId" from-field="parameters.rateTypeId"/>
-                    <set field="teUpdMap.partyId" from-field="userLogin.partyId"/>
-                    <call-service service-name="updateTimeEntry" in-map-name="teUpdMap"/>
-                </else>
-            </if-compare>
-            <else>
-                <if-compare field="hours" operator="not-equals" value="0">
-                    <set-service-fields service-name="createTimeEntry" map="parameters" to-map="teCreMap"/>
-                    <set field="teCreMap.hours" from-field="hours" type="Double"/>
-                    <set field="teCreMap.fromDate" from-field="fromDate"/>
-                    <set field="teCreMap.partyId" from-field="userLogin.partyId"/>
-                    <call-service service-name="createTimeEntry" in-map-name="teCreMap"/>
-                </if-compare>
-            </else>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="createTimeEntryInTimesheet" short-description="Creates TimeEntry and searches for a timesheetId if not provided">
-        <if-not-empty field="parameters.fromDate">
-            <if-empty field="parameters.timesheetId">
-                <entity-condition entity-name="Timesheet" list="timesheets">
-                    <condition-list combine="and">
-                        <condition-expr field-name="fromDate" operator="less-equals" from-field="parameters.fromDate"/>
-                        <condition-expr field-name="thruDate" operator="greater-equals" from-field="parameters.fromDate"/>
-                        <condition-expr field-name="partyId" operator="equals" from-field="parameters.partyId"/>
-                    </condition-list>
-                </entity-condition>
-                <if-not-empty field="timesheets">
-                    <!-- use existing timesheet -->
-                    <first-from-list list="timesheets" entry="timesheet"/>
-                    <if-compare field="timesheet.statusId" value="TIMESHEET_IN_PROCESS" operator="equals">
-                        <set field="parameters.timesheetId" from-field="timesheet.timesheetId"/>
-                        <else>
-                            <add-error>
-                                <fail-property resource="ProjectMgrUiLabels" property="ProjectMgrCannotAddToTimesheet"/>
-                            </add-error>
-                            <check-errors/>
-                        </else>
-                    </if-compare>
-                    <else>
-                        <!-- create new timesheet -->
-                        <set field="parameters.requiredDate" from-field="parameters.fromDate"/>
-                        <call-simple-method method-name="createTimesheetForThisWeek" xml-resource="component://workeffort/minilang/timesheet/TimesheetServices.xml"/>
-                        <set field="parameters.timesheetId" from-field="newEntity.timesheetId"/>
-                    </else>
-                </if-not-empty>
-            </if-empty>
-        </if-not-empty>
-        <!-- get role for this party in this project -->
-        <if-empty field="parameters.roleTypeId">
-          <entity-and entity-name="ProjectPartyAndPhaseAndTask" list="taskRoles" filter-by-date="true">
-              <field-map field-name="partyId" from-field="parameters.partyId"/>
-              <field-map field-name="workEffortId" from-field="parameters.workEffortId"/>
-          </entity-and>
-          <first-from-list list="taskRoles" entry="taskRole"/>
-          <set field="parameters.roleTypeId" from-field="taskRole.roleTypeId"/>
-          <set field="parameters.statusId" value="PAS_ASSIGNED"/>
-          <call-simple-method method-name="assignPartyToWorkEffort" xml-resource="component://workeffort/minilang/workeffort/WorkEffortSimpleServices.xml"/>
-        </if-empty>
-        <call-simple-method method-name="createTimeEntry" xml-resource="component://workeffort/minilang/timesheet/TimesheetServices.xml"/>
-    </simple-method>
-
-    <simple-method method-name="addProjectTimeToNewInvoice" short-description="add all reported time on all completed timesheets from all workefforts for a project">
-        <!-- recreate the invoice if still in preparation in order to correct errors. -->
-        <if-compare operator="equals" value="Y" field="parameters.reCreate">
-            <entity-one entity-name="Invoice" value-field="invoice"/>
-            <if-empty field="invoice">
-                <add-error>
-                    <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetCannotFindInvoice"/>
-                </add-error>
-                <check-errors/>
-            </if-empty>
-
-            <call-simple-method method-name="checkInvoiceStatusInProgress" xml-resource="component://accounting/minilang/invoice/InvoiceServices.xml"/>
-
-            <entity-and entity-name="TimeEntry" list="entries">
-                <field-map field-name="invoiceId" from-field="parameters.invoiceId"/>
-            </entity-and>
-            <iterate list="entries" entry="timeEntry">
-                <clear-field field="timeEntry.invoiceId"/>
-                <clear-field field="timeEntry.invoiceItemSeqId"/>
-                <store-value value-field="timeEntry"/>
-            </iterate>
-            <set field="removeItems.invoiceId" from-field="parameters.invoiceId"/>
-            <remove-by-and entity-name="InvoiceItem" map="removeItems"/>
-            <set field="notFirst" value="Y"/><!-- do not create, only add -->
-        </if-compare>
-        <entity-condition entity-name="ProjectPhaseTaskAndTimeEntryTimeSheet" list="tasks">
-            <condition-list combine="and">
-                <condition-expr field-name="projectId" operator="equals" from-field="parameters.projectId"/>
-                <condition-expr field-name="invoiceId" operator="equals" from-field="nullField"/>
-                <condition-expr field-name="timesheetStatusId" operator="equals" value="TIMESHEET_COMPLETED"/>
-                <condition-expr field-name="fromDate" operator="less" from-field="parameters.thruDate" ignore-if-empty="true"/>
-            </condition-list>
-            <order-by field-name="workEffortId"/>
-        </entity-condition>
-        <if-empty field="tasks">
-            <add-error>
-                <fail-property resource="ProjectMgrUiLabels" property="ProjectMgrNoTimeentryItemsFound"/>
-            </add-error>
-            <check-errors/>
-        </if-empty>
-        <iterate list="tasks" entry="task">
-            <if-empty field="notFirst">
-                <!-- first time so create invoice -->
-                <set-service-fields service-name="addWorkEffortTimeToNewInvoice" map="parameters" to-map="addTaskToNewInvoice"/>
-                <set field="addTaskToNewInvoice.workEffortId" from-field="task.workEffortId"/>
-                <set field="addTaskToNewInvoice.combineInvoiceItem" value="Y"/>
-                <set field="addTaskToNewInvoice.thruDate" from-field="parameters.thruDate"/>
-                <call-service service-name="addWorkEffortTimeToNewInvoice" in-map-name="addTaskToNewInvoice">
-                    <result-to-field result-name="invoiceId" field="parameters.invoiceId"/>
-                </call-service>
-                <set field="addTaskToInvoice.combineInvoiceItem" value="Y"/>
-                <field-to-result field="parameters.invoiceId" result-name="invoiceId"/>
-                <set field="notFirst" value="Y"/>
-                <else>
-                    <if>
-                        <condition>
-                            <or>
-                                <if-empty field="oldWorkeffortId"/>
-                                <if-compare-field operator="not-equals" field="oldWorkeffortId" to-field="task.workEffortId"/>
-                            </or>
-                        </condition>
-                        <then>
-                            <!-- add to created invoice -->
-                            <set field="addTaskToInvoice.combineInvoiceItem" value="Y"/>
-                            <set field="addTaskToInvoice.invoiceId" from-field="parameters.invoiceId"/>
-                            <set field="addTaskToInvoice.workEffortId" from-field="task.workEffortId"/>
-                            <set field="addTaskToInvoice.thruDate" from-field="parameters.thruDate"/>
-                            <call-service service-name="addWorkEffortTimeToInvoice" in-map-name="addTaskToInvoice"/>
-                        </then>
-                    </if>
-                    <set field="oldWorkeffortId" from-field="task.workEffortId"/>
-                </else>
-            </if-empty>
-        </iterate>
-    </simple-method>
-
-    <simple-method method-name="addProjectTimeToInvoice" short-description="add all reported time on all completed timesheets
-        from all workefforts for a project to an existing invoice">
-        <entity-one entity-name="Invoice" value-field="invoice"/>
-        <if-empty field="invoice">
-            <add-error>
-                <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetCannotFindInvoice"/>
-            </add-error>
-            <check-errors/>
-        </if-empty>
-
-        <call-simple-method method-name="checkInvoiceStatusInProgress" xml-resource="component://accounting/minilang/invoice/InvoiceServices.xml"/>
-
-        <entity-condition entity-name="ProjectPhaseTaskAndTimeEntryTimeSheet" list="tasks">
-            <condition-list combine="and">
-                <condition-expr field-name="projectId" operator="equals" from-field="parameters.projectId"/>
-                <condition-expr field-name="invoiceId" operator="equals" from-field="nullField"/>
-                <condition-expr field-name="timesheetStatusId" operator="equals" value="TIMESHEET_COMPLETED"/>
-                <condition-expr field-name="fromDate" operator="less" from-field="parameters.thruDate" ignore-if-empty="true"/>
-            </condition-list>
-            <order-by field-name="workEffortId"/>
-        </entity-condition>
-
-        <if-empty field="tasks">
-            <add-error>
-                <fail-property resource="ProjectMgrUiLabels" property="ProjectMgrNoTimeentryItemsFound"/>
-            </add-error>
-            <check-errors/>
-        </if-empty>
-
-        <iterate list="tasks" entry="task">
-            <if>
-                <condition>
-                    <or>
-                        <if-empty field="oldWorkeffortId"/>
-                        <if-compare-field operator="not-equals" field="oldWorkeffortId" to-field="task.workEffortId"/>
-                    </or>
-                </condition>
-                <then>
-                    <!-- add to created invoice -->
-                    <set field="addTaskToInvoice.combineInvoiceItem" value="Y"/>
-                    <set field="addTaskToInvoice.invoiceId" from-field="parameters.invoiceId"/>
-                    <set field="addTaskToInvoice.workEffortId" from-field="task.workEffortId"/>
-                    <set field="addTaskToInvoice.thruDate" from-field="parameters.thruDate"/>
-                    <call-service service-name="addWorkEffortTimeToInvoice" in-map-name="addTaskToInvoice"/>
-                </then>
-            </if>
-            <set field="oldWorkeffortId" from-field="task.workEffortId"/>
-        </iterate>
-    </simple-method>
-
-    <simple-method method-name="addValidationPartiesToTask" short-description="SECA to add either project-testing or -approval parties to a task when a task is set to complete">
-        <!-- check if this is the last party which completed his task -->
-        <entity-condition entity-name="WorkEffortPartyAssignment" list="openTasks" filter-by-date="true">
-            <condition-list combine="and">
-                <condition-expr field-name="workEffortId" operator="equals" from-field="parameters.workEffortId"/>
-                <condition-expr field-name="statusId" operator="not-equals" value="PAS_COMPLETED"/>
-                <condition-expr field-name="partyId" operator="not-equals" from-field="parameters.partyId"/>
-            </condition-list>
-        </entity-condition>
-        <if-empty field="openTasks">
-            <set field="getProject.taskId" from-field="parameters.workEffortId"/>
-            <call-service service-name="getProjectIdAndNameFromTask" in-map-name="getProject">
-            <result-to-field result-name="projectId"/>
-            </call-service>
-            <!-- see who is reponsible for testing/validation in this project -->
-            <entity-condition entity-name="WorkEffortPartyAssignment" list="assigns" filter-by-date="true">
-                <condition-list combine="and">
-                    <condition-expr field-name="workEffortId" operator="equals" from-field="projectId"/>
-                    <condition-expr field-name="partyId" operator="not-equals" from-field="parameters.partyId"/><!-- should not test/val own work -->
-                    <condition-list combine="or">
-                        <condition-expr field-name="roleTypeId" operator="equals" value="PROVIDER_VALIDATOR"/>
-                        <condition-expr field-name="roleTypeId" operator="equals" value="PROVIDER_TESTER"/>
-                    </condition-list>
-                </condition-list>
-            </entity-condition>
-            <if-not-empty field="assigns">
-                <set field="addAssign.workEffortId" from-field="parameters.workEffortId"/>
-                <set field="addAssign.statusId" value="PAS_ASSIGNED"/>
-                <iterate list="assigns" entry="assign">
-                    <make-value entity-name="WorkEffortPartyAssignment" value-field="newAssign"/>
-                    <set field="newAssign.workEffortId" from-field="parameters.workEffortId"/>
-                    <set field="newAssign.partyId" from-field="assign.partyId"/>
-                    <set field="newAssign.roleTypeId" from-field="assign.roleTypeId"/>
-                    <set field="newAssign.assignedByUserLoginId" value="system"/>
-                    <now-timestamp field="newAssign.fromDate"/>
-                    <set field="newAssign.statusId" value="PAS_ASSIGNED"/>
-                    <create-value value-field="newAssign"/>
-                </iterate>
-                <else>
-                    <log level="info" message="No validation parties defined in this project: no validation parties added...."/>
-                </else>
-            </if-not-empty>
-            <else>
-                <log level="info" message="Not the last party who completes his task: validation parties not added...."/>
-            </else>
-        </if-empty>
-    </simple-method>
-
-</simple-methods>
diff --git a/projectmgr/servicedef/services.xml b/projectmgr/servicedef/services.xml
index 038ceb5..1c55aed 100644
--- a/projectmgr/servicedef/services.xml
+++ b/projectmgr/servicedef/services.xml
@@ -22,17 +22,18 @@
     xsi:noNamespaceSchemaLocation="https://ofbiz.apache.org/dtds/services.xsd">
     <description>Project Manager service definitions.</description>
 
-    <service name="createTimeEntryInTimesheet" default-entity-name="TimeEntry" engine="simple" auth="true"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="createTimeEntryInTimesheet">
+    <service name="createTimeEntryInTimesheet" default-entity-name="TimeEntry" engine="groovy" auth="true"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="createTimeEntryInTimesheet">
         <description>Creates TimeEntry and searches for a timesheetId, if required it will create a weekly time sheet, it also assigns the party to the task</description>
         <permission-service service-name="projectMgrPermission" main-action="CREATE"/>
-        <auto-attributes include="pk" mode="OUT" optional="false"/>
+        <auto-attributes include="pk" mode="OUT"/>
         <auto-attributes include="nonpk" mode="IN" optional="true"/>
         <attribute name="roleTypeId" type="String" mode="IN" optional="true"/>
         <attribute name="timesheetId" type="String" mode="OUT" optional="true"/>
         <attribute name="fromDate" type="Timestamp" mode="OUT" optional="true"/>
     </service>
 
+    <!-- FIXME -->
     <service name="createProjectTimesheet" engine="simple" default-entity-name="Timesheet"
         location="component://workeffort/minilang/timesheet/TimesheetServices.xml" invoke="createTimesheetForThisWeek">
         <description>Creates Timesheet for this week if no required date specified.</description>
@@ -45,6 +46,7 @@
         <attribute name="requiredDate" type="Timestamp" mode="IN" optional="true"/>
     </service>
 
+    <!-- FIXME -->
     <service name="projectMgrPermission" engine="simple"
         location="component://projectmgr/minilang/ProjectPermissionServices.xml" invoke="projectMgrPermission">
         <implements service="permissionInterface"/>
@@ -56,17 +58,17 @@
         <attribute name="timesheetId" type="String" mode="IN" optional="true"/>
         <attribute name="timeEntryId" type="String" mode="IN" optional="true"/>
     </service>
-    
+    <!-- FIXME -->
     <service name="projectMgrRequestPermission" engine="simple"
         location="component://projectmgr/minilang/ProjectPermissionServices.xml" invoke="projectMgrRequestPermission">
         <implements service="permissionInterface"/>
     </service>
-    
-    <service name="updateTimeEntryByWorkeffort" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="updateTimeEntryByWorkeffort">
+
+    <service name="updateTimeEntryByWorkEffort" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="updateTimeEntryByWorkEffort">
         <description>Update workeffort by workeffortId and timesheetId </description>
         <permission-service service-name="projectMgrPermission" main-action="UPDATE"/>
-        <attribute name="timesheetId" type="String" mode="INOUT" optional="false"/>
+        <attribute name="timesheetId" type="String" mode="INOUT"/>
         <attribute name="workEffortId" type="String" mode="INOUT" optional="true"/><!-- when empty will be ignored -->
         <attribute name="fromDate" type="Timestamp" mode="INOUT" optional="true"/>
         <attribute name="rateTypeId" type="String" mode="IN" optional="true"/>
@@ -81,8 +83,8 @@
         <attribute name="checkComplete" type="String" mode="IN" optional="true"/>
     </service>
 
-    <service name="getProject" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="getProject">
+    <service name="getProject" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="getProject">
         <description>Get project information and related phase and task info.</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <attribute name="projectId" type="String" mode="INOUT" optional="true"/>
@@ -91,8 +93,8 @@
     </service>
 
     <service name="createProject" engine="groovy" default-entity-name="WorkEffort"
-        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServices.groovy" invoke="createProject">
-        <description>Create a new Project</description>
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="createProject">
+        <description>Create a new Project.</description>
         <permission-service service-name="projectMgrPermission" main-action="CREATE"/>
         <implements service="interfaceWorkEffort"/>
         <auto-attributes mode="INOUT" include="pk" optional="true"/>
@@ -110,8 +112,9 @@
         <override name="workEffortName" optional="false"/>
         <override name="currentStatusId" optional="false"/>
     </service>
-    <service name="updateProject" default-entity-name="WorkEffort" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="updateProject">
+
+    <service name="updateProject" default-entity-name="WorkEffort" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="updateProject">
         <description>Update a Project</description>
         <permission-service service-name="projectMgrPermission" main-action="UPDATE"/>
         <implements service="interfaceWorkEffort"/>
@@ -120,21 +123,27 @@
         <attribute name="clientBillingPartyId" type="String" mode="IN" optional="true"/>
         <attribute name="emailAddress" type="String" mode="IN" optional="true"/>
     </service>
+    <service name="updateProjectRole" default-entity-name="WorkEffortPartyAssignment" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="updateProjectRole">
+        <description>Update a Project Role</description>
+        <permission-service service-name="projectMgrPermission" main-action="UPDATE"/>
+        <implements service="createWorkEffortPartyAssignment"/>
+    </service>
 
-    <service name="createProjectTask" engine="simple" default-entity-name="WorkEffort"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="createProjectTask">
+    <service name="createProjectTask" engine="groovy" default-entity-name="WorkEffort"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="createProjectTask">
         <description>Create a new task and optionally assign to a resource.</description>
         <permission-service service-name="projectMgrPermission" main-action="CREATE"/>
         <implements service="interfaceWorkEffort"/>
         <auto-attributes mode="INOUT" include="pk" optional="true"/>
         <attribute name="partyId" type="String" mode="IN" optional="true"/>
         <attribute name="roleTypeId" type="String" mode="IN" optional="true"/>
-        <attribute name="statusId" type="String" mode="IN" optional="true"/>
+        <attribute name="statusId" type="String" mode="IN" default-value="PTS_CREATED"/>
         <attribute name="fromDate" type="Timestamp" mode="OUT" optional="true"/>
         <attribute name="quickAssignPartyId" type="String" mode="IN" optional="true"/>
         <attribute name="requirementId" type="String" mode="IN" optional="true"/>
         <attribute name="communicationEventId" type="String" mode="IN" optional="true"/>
-        <attribute name="skillTypeId" type="String" mode="IN" optional="true"/>
+        <attribute name="skillTypeId" type="String" mode="IN" default-value="_NA_"/>
         <attribute name="estimatedHours" type="Double" mode="IN" optional="true"/>
         <override name="workEffortTypeId" optional="false"/>
         <override name="workEffortName" optional="false"/>
@@ -142,33 +151,31 @@
         <override name="workEffortParentId" optional="false"/>
     </service>
 
-    <service name="copyProject" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="copyProject">
+    <service name="copyProject" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="copyProject">
         <description>Copy a project planning data but ignore the actual data.</description>
         <permission-service service-name="projectMgrPermission" main-action="CREATE"/>
-        <attribute name="projectId" type="String" mode="INOUT" optional="false"/>
+        <attribute name="projectId" type="String" mode="INOUT"/>
         <attribute name="fromDate" type="Timestamp" mode="OUT" optional="true"/>
         <attribute name="workEffortId" type="String" mode="OUT" optional="true"/>
     </service>
 
-    <service name="copyProjectToTemplate" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="copyProjectToTemplate">
+    <service name="copyProjectToTemplate" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="copyProject">
         <description>Copy a project planning data to a template project.</description>
-        <permission-service service-name="projectMgrPermission" main-action="CREATE"/>
-        <attribute name="projectId" type="String" mode="INOUT" optional="false"/>
-        <attribute name="fromDate" type="Timestamp" mode="OUT" optional="true"/>
-        <attribute name="workEffortId" type="String" mode="OUT" optional="true"/>
+        <implements service="copyProject"/>
+        <attribute name="toTemplate" type="String" mode="IN" default-value="Y"/>
     </service>
 
-    <service name="scheduleProject" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="scheduleProject">
+    <service name="scheduleProject" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="scheduleProject">
         <description>(re) calculate the estimated start and enddates of tasks within a project</description>
         <permission-service service-name="projectMgrPermission" main-action="UPDATE"/>
         <attribute name="projectId" type="String" mode="INOUT" optional="true"/>
     </service>
 
-    <service name="getProjectIdAndNameFromTask" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="getProjectIdAndNameFromTask">
+    <service name="getProjectIdAndNameFromTask" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="getProjectIdAndNameFromTask">
         <description>Get the projectId and Name when a phase or task is provided.</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <attribute name="taskId" type="String" mode="INOUT" optional="true"/>
@@ -181,24 +188,24 @@
         <attribute name="taskWbsId" type="String" mode="OUT" optional="true"/>
     </service>
 
-    <service name="getProjectPhaseList" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="getProjectPhaseList">
+    <service name="getProjectPhaseList" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="getProjectPhaseList">
         <description>Get project phase information and related task info.</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <attribute name="projectId" type="String" mode="INOUT" optional="true"/>
         <attribute name="phaseList" type="List" mode="OUT" optional="true"/>
     </service>
 
-    <service name="getProjectTaskList" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="getProjectTaskList">
+    <service name="getProjectTaskList" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="getProjectTaskList">
         <description>Get project phase information and related task info.</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <attribute name="projectId" type="String" mode="INOUT" optional="true"/>
         <attribute name="taskList" type="List" mode="OUT" optional="true"/>
     </service>
 
-    <service name="getProjectTask" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="getProjectTask">
+    <service name="getProjectTask" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="getProjectTask">
         <description>Get project task information and related timesheet info.</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <attribute name="hoursPartyId" type="String" mode="IN" optional="true"/>
@@ -208,8 +215,8 @@
         <attribute name="taskInfo" type="Map" mode="OUT" optional="true"/>
     </service>
 
-    <service name="getProjectsByParties" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="getProjectsByParties">
+    <service name="getProjectsByParties" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="getProjectsByParties">
         <description>Get project information by party</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <attribute name="projectId" type="String" mode="INOUT" optional="true"/>
@@ -217,8 +224,8 @@
         <attribute name="projectParties" type="List" mode="OUT" optional="true"/>
     </service>
 
-    <service name="getTasksByParties" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="getTasksByParties">
+    <service name="getTasksByParties" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="getTasksByParties">
         <description>Get project information by party</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <attribute name="workEffortId" type="String" mode="INOUT" optional="true"/>
@@ -226,8 +233,8 @@
         <attribute name="taskParties" type="List" mode="OUT" optional="true"/>
     </service>
 
-    <service name="updateTaskAndRelatedInfo" default-entity-name="WorkEffort" engine="simple"
-            location="component://projectmgr/minilang/ProjectServices.xml" invoke="updateTaskAndRelatedInfo">
+    <service name="updateTaskAndRelatedInfo" default-entity-name="WorkEffort" engine="groovy"
+            location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="updateTaskAndRelatedInfo">
         <description>Update a task and related info</description>
         <permission-service service-name="projectMgrPermission" main-action="VIEW"/>
         <implements service="interfaceWorkEffort"/>
@@ -239,8 +246,8 @@
         <attribute name="estimatedDuration" mode="IN" type="Double" optional="true"/>
     </service>
  
-     <service name="updateTaskAssigment" default-entity-name="WorkEffortPartyAssignment" engine="simple"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="updateTaskAssigment">
+     <service name="updateTaskAssigment" default-entity-name="WorkEffortPartyAssignment" engine="groovy"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="updateTaskAssigment">
         <description>Update a WorkEffortPartyAssignment Entity, including set enddate and create new</description>
         <permission-service service-name="projectMgrPermission" main-action="UPDATE"/>
         <attribute name="workEffortId" type="String" mode="INOUT"/>
@@ -254,26 +261,26 @@
         <attribute name="delegateReasonEnumId" type="String" mode="IN" optional="true"/>
         <attribute name="comments" type="String" mode="IN" optional="true"/>
     </service>
-    <service name="addProjectTimeToNewInvoice" engine="simple" auth="true"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="addProjectTimeToNewInvoice">
+    <service name="addProjectTimeToNewInvoice" engine="groovy" auth="true"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="addProjectTimeToInvoice">
         <description>Add Project Time to a new Invoice</description>
-        <attribute name="projectId" type="String" mode="IN" optional="false"/>
+        <attribute name="projectId" type="String" mode="IN"/>
         <attribute name="partyIdFrom" type="String" mode="IN" optional="true"/>
         <attribute name="partyId" type="String" mode="IN" optional="true"/>
         <attribute name="thruDate" type="Timestamp" mode="IN" optional="true"/>
         <attribute name="invoiceId" type="String" mode="INOUT" optional="true"/>
-        <attribute name="reCreate" type="String" mode="IN" optional="true"/>
+        <attribute name="reCreate" type="String" mode="IN" default-value="Y"/>
     </service>
-    <service name="addProjectTimeToInvoice" engine="simple" auth="true"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="addProjectTimeToInvoice">
+    <service name="addProjectTimeToInvoice" engine="groovy" auth="true"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="addProjectTimeToInvoice">
         <description>Add Project Time to an existing Invoice. The billed party must be the same between the two invoice.</description>
-        <attribute name="projectId" type="String" mode="IN" optional="false"/>
-        <attribute name="invoiceId" type="String" mode="IN" optional="false"/>
-        <attribute name="billedPartyId" type="String" mode="IN" optional="false"/>
+        <attribute name="projectId" type="String" mode="IN"/>
+        <attribute name="invoiceId" type="String" mode="IN"/>
+        <attribute name="billedPartyId" type="String" mode="IN"/>
         <attribute name="thruDate" type="Timestamp" mode="IN" optional="true"/>
     </service>
-    <service name="addValidationPartiesToTask" engine="simple" auth="true"
-        location="component://projectmgr/minilang/ProjectServices.xml" invoke="addValidationPartiesToTask">
+    <service name="addValidationPartiesToTask" engine="groovy" auth="true"
+        location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy" invoke="addValidationPartiesToTask">
         <description>SECA to add either project-testing or -approval parties to a task when a task is set to complete</description>
         <attribute name="workEffortId" type="String" mode="IN" optional="true"/>
         <attribute name="partyId" type="String" mode="IN" optional="true"/>
diff --git a/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy b/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy
new file mode 100644
index 0000000..20db675
--- /dev/null
+++ b/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy
@@ -0,0 +1,1097 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+package org.apache.ofbiz.projectmgr
+
+import org.apache.ofbiz.base.util.UtilDateTime
+import org.apache.ofbiz.base.util.UtilValidate
+import org.apache.ofbiz.entity.GenericValue
+import org.apache.ofbiz.entity.condition.EntityCondition
+import org.apache.ofbiz.entity.condition.EntityConditionBuilder
+import org.apache.ofbiz.project.Various
+
+import java.sql.Timestamp
+
+/**
+ * Create a project
+ * @return 'Success response containing the projectId, error response otherwise.
+ */
+Map createProject() {
+    Map serviceResult
+    if (parameters.templateId) {
+        serviceResult = run service: 'copyProjet', with: [*: parameters,
+                                                          projectId: parameters.templateId]
+    } else {
+        serviceResult = run service: 'createWorkEffort', with: [*: parameters,
+                                                                currentStatusId: 'PRJ_ACTIVE']
+    }
+    Map serviceMap = [*: parameters,
+                      workEffortId: serviceResult.workEffortId]
+
+    // Add roles
+    if (parameters.organizationPartyId) {
+        run service: 'updateProjectRole', with: [*: serviceMap,
+                                                 partyId: parameters.organizationPartyId,
+                                                 roleTypeId: 'INTERNAL_ORGANIZATIO']
+    }
+    if (parameters.clientBillingPartyId) {
+        run service: 'updateProjectRole', with: [*: serviceMap,
+                                                 partyId: parameters.clientBillingPartyId,
+                                                 roleTypeId: 'CLIENT_BILLING']
+    }
+
+    // create new work effort's e-mail address
+    if (parameters.emailAddress) {
+        if (!UtilValidate.isEmail(parameters.emailAddress)) {
+            return error(label('PartyUiLabels', 'PartyEmailAddressNotFormattedCorrectly'))
+        }
+        run service: 'createWorkEffortEmailAddress', with: serviceMap
+    }
+    return success([projectId: serviceMap.workEffortId, workEffortId: serviceMap.workEffortId])
+}
+
+/**
+ * Update a project
+ * @return 'Success response after updating, error response otherwise.
+ */
+Map updateProject() {
+    run service: 'updateWorkEffort', with: parameters
+
+    // Add roles
+    if (parameters.organizationPartyId) {
+        run service: 'updateProjectRole', with: [*: parameters,
+                                                 partyId: parameters.organizationPartyId,
+                                                 roleTypeId: 'INTERNAL_ORGANIZATIO']
+    }
+    if (parameters.clientBillingPartyId) {
+        run service: 'updateProjectRole', with: [*: parameters,
+                                                 partyId: parameters.clientBillingPartyId,
+                                                 roleTypeId: 'CLIENT_BILLING']
+    }
+
+    // update new work effort's e-mail address
+    if (parameters.emailAddress) {
+        if (!UtilValidate.isEmail(parameters.emailAddress)) {
+            return error(label('PartyUiLabels', 'PartyEmailAddressNotFormattedCorrectly'))
+        }
+        GenericValue existEmailAddress = from('WorkEffortContactMechView')
+                .where(workEffortId: parameters.workEffortId,
+                        contactMechTypeId: 'EMAIL_ADDRESS')
+                .filterByDate()
+                .queryFirst()
+        if (existEmailAddress) {
+            run service: 'updateWorkEffortEmailAddress', with: [*: parameters,
+                                                                oldContactMechId: existEmailAddress.contactMechId]
+        } else {
+            run service: 'createWorkEffortEmailAddress', with: parameters
+        }
+    }
+    return success()
+}
+
+/**
+ * update/create a specif role and type for a project
+ * @return 'Success response after updating, error response otherwise.
+ */
+Map updateProjectRole() {
+    GenericValue workEffortPartyAssignment = from('WorkEffortPartyAssignment')
+            .where(workEffortId: parameters.workEffortId,
+                    roleTypeId: parameters.roleTypeId)
+            .filterByDate()
+            .queryFirst()
+
+    if (workEffortPartyAssignment) {
+        if (parameters.partyId != workEffortPartyAssignment.partyId) {
+            run service: 'expireWorkEffortPartyAssignment', with: workEffortPartyAssignment.getAllFields()
+        } else {
+            return success()
+        }
+    }
+    run service: 'assignPartyToWorkEffort', with: parameters
+    return success()
+}
+
+/**
+ * Create a project task and optionally assign
+ * @return 'Success response after creation, error response otherwise.
+ */
+Map createProjectTask() {
+    // create task
+    Map serviceResult = run service: 'createWorkEffort', with: parameters
+    Map serviceMap = [*: parameters,
+                       workEffortId: serviceResult.workEffortId]
+
+    // optionally assign to party
+    if (parameters.partyId) {
+        run service: 'assignPartyToWorkEffort', with: serviceMap
+    }
+
+    // optionally enter estimated time and required skill -->
+    if (parameters.estimatedHours) {
+        run service: 'createWorkEffortSkillStandard', with: [*: serviceMap,
+                                                             estimatedDuration: parameters.estimatedHours]
+    }
+    return success([workEffortId: serviceMap.workEffortId])
+}
+
+/**
+ * Update the task and when info is provided update the related information too
+ * @return 'Success response after updating, error response otherwise.
+ */
+Map updateTaskAndRelatedInfo() {
+    run service: 'updateWorkEffort', with: parameters
+    if (parameters.estimatedDuration) {
+        Map serviceMap = [*: parameters]
+        if (!parameters.skillTypeId) {
+            GenericValue workEffortSkillStandard = from('WorkEffortSkillStandard')
+                    .where(workEffortId: parameters.workEffortId)
+                    .cache()
+                    .queryFirst()
+            serviceMap.skillTypeId = workEffortSkillStandard ? workEffortSkillStandard.skillTypeId : '_NA_'
+        }
+        String actionNeed = (from('WorkEffortSkillStandard')
+                .where(workEffortId: parameters.workEffortId,
+                        skillTypeId: serviceMap.skillTypeId)
+                .queryCount() > 0) ? 'update' : 'create'
+        run service: "${actionNeed}WorkEffortSkillStandard", with: serviceMap
+    }
+    return success()
+}
+
+/**
+ * Update task to resource assignment, if required create a new one by re-assigment
+ * @return 'Success response after updating, error response otherwise.
+ */
+Map updateTaskAssigment() {
+    // check if a change in partyId Or roleTypeId: need to delete and create new
+    Timestamp fromDate = parameters.fromDate
+    if ((parameters.newPartyId && parameters.partyId != parameters.newPartyId)
+            || (parameters.newRoleTypeId && parameters.roleTypeId != parameters.newRoleTypeId)) {
+        // roleType and/or partyId changed: end old and create new assign
+        run service: 'expireWorkEffortPartyAssignment', with: parameters
+        Map serviceResult = run service: 'createWorkEffortPartyAssignment', with: [*: parameters,
+                                                                                   partyId: parameters.newPartyId,
+                                                                                   statusId: 'PAS_ASSIGNED',
+                                                                                   roleTypeId: parameters.newRoleTypeId]
+        fromDate = serviceResult.fromDate
+    } else {
+        GenericValue partyAssignment = from('WorkEffortAndPartyAssign').where(parameters).queryOne()
+        if (partyAssignment) {
+            if (parameters.statusId == 'PAS_ENDED') {
+                run service: 'expireWorkEffortPartyAssignment', with: parameters
+            }
+            if (parameters.statusId == 'PAS_COMPLETED') {
+                return updateTaskStatusToComplete(parameters.workEffortId)
+            }
+        }
+        String serviceName = partyAssignment ? 'updateWorkEffortPartyAssignment' : 'assignPartyToWorkEffort'
+        run service: serviceName, with: parameters
+    }
+    return success([workEffortId: parameters.workEffortId, fromDate: fromDate])
+}
+
+/**
+ * Check partyAssignments on a task, if all completes set task status to completed and set actual completionDate to now
+ * @return Success response after updating, error response otherwise.
+ */
+Map updateTaskStatusToComplete(String workEffortId) {
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        EQUALS(workEffortId: workEffortId)
+        NOT_EQUAL(statusId: 'PAS_COMPLETED')
+    }
+    boolean canComplete = from('WorkEffortPartyAssignment')
+            .where(condition)
+            .filterByDate()
+            .queryOne() == 0
+    if (canComplete) {
+        run service: 'updateWorkEffort', with: [
+                workEffortId: workEffortId,
+                currentStatusId: 'PTS_COMPLETED',
+                actualCompletionDate: UtilDateTime.nowTimestamp()]
+
+        // check for related customer request, set these too to completed
+        from('CustRequestWorkEffort')
+                .where(workEffortId: workEffortId)
+                .queryList()
+                .each {
+                    run service: 'updateCustRequest', with: [custRequestId: it.custRequestId,
+                                                             statusId: 'CRQ_COMPLETED']
+                }
+    }
+    return success([workEffortId: workEffortId])
+}
+
+/**
+ * Project Scheduler sets the planning dates according task requirements and available resources
+ * theory behind the program
+ * - - - - - - - - - - - - -
+ * (program under development)
+ * Assumptions for tasks and resources
+ * 1. a workday has 8 hours.
+ * 2. a workweek has 40 hours and 5 days.
+ * 3. The order of the execution of the tasks is set by the workeffort association.
+ * 4. The default start of the project is today
+ * 5. default length of a task is 3 day if not planned hours entered
+ *
+ * The steps of the program are:
+ *    1. read all tasks  and check if there are predesessors, when not set he estimated dates
+ *    for critical path processing:
+ *      * ES - Earliest Start time
+ *      * EF - Earliest Finish time
+ *      * LS - Latest Start time
+ *      * LF - Latest Finish time
+ *
+ * EF = LF task is on the critical path
+ *
+ * 2. call a recursive java function to set all the dependant tasks.
+ */
+Map scheduleProject() {
+    // find a starting point being either the estimated start date of a project or the earliest actual start date.
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        NOT_EQUAL(actualStartDate: null)
+    }
+    List tasks = from('ProjectAndPhaseAndTask')
+            .where(condition)
+            .orderBy('-actualStartDate')
+            .queryList()
+
+    Timestamp generalStartDate = null
+    Timestamp startDate = null
+    String taskId = null
+    // remove all estimated dates
+    if (tasks) {
+        startDate = tasks[0].actualStartDate
+        taskId = tasks[0].workEffortId
+        tasks.each {
+            tasks.estimatedStartDate = null
+            tasks.estimatedCompletionDate = null
+        }
+    } else {
+        generalStartDate = UtilDateTime.nowTimestamp()
+    }
+
+    while (!generalStartDate) {
+        BigDecimal highestHours
+        List assocs = from('WorkEffortAssoc')
+                .where(workEffortId: taskId)
+                .queryList()
+        if (assocs) {
+            assocs.each {
+                BigDecimal hours = 0
+                Map task = run service: 'getProjectTask', with: [taskId: it.workEffortIdFrom]
+                if (task.estimatedHours && task.actualHours) {
+                    hours = (task.estimatedHours < task.actualHours) ? task.actualHours : task.estimatedHours
+                } else {
+                    hours = task.actualHours ?: 16
+                }
+                if (!highestHours || highestHours < hours) {
+                    highestHours = hours
+                    preDesessorId = task.taskId
+                }
+            }
+            BigDecimal taskDays = -(highestHours / 8)
+            startDate = UtilDateTime.addDaysToTimestamp(startDate, taskDays)
+        } else {
+            GenericValue workEffort = from('WorkEffort').where(workEffortId: taskId).queryOne()
+            if (workEffort.parentWorkEffortId) {
+                taskId = workEffort.parentWorkEffortId
+            } else {
+                generalStartDate = startDate
+            }
+        }
+    }
+
+    // create the tasklist
+    Timestamp now = UtilDateTime.nowTimestamp()
+    GenericValue project = from('WorkEffort').where(workEffortId: parameters.projectId).queryOne()
+    project.getRelated('ChildWorkEffort', null, null, false)
+            .each {
+                it.getRelated('ChildWorkEffort', null, null, false)
+                        .each { task ->
+                            if (from('WorkEffortAssoc').where(workEffortIdFrom: task.workEffortId).queryOne()) {
+                                // no predecessors so i can set the dates
+                                run service: 'updateWorkEffort', with: [workEffortId: task.workEffortId,
+                                                                        estimatedStartDate: now,
+                                                                        estimatedCompletionDate: Various.calculateCompletionDate(task, now)]
+                                Various.setDatesFollowingTasks(from('WorkEffort').where(workEffortId: task.workEffortId).queryOne())
+                            }
+                        }
+            }
+    return success()
+}
+
+/**
+ *  Update workeffort by workEffortId and timesheetId
+ * @return 'Success response after updating, error response otherwise.
+ */
+Map updateTimeEntryByWorkEffort() {
+    if (!parameters.workEffortId || parameters.workEffortId == 'Totals') {
+        return success([timesheetId: parameters.timesheetId])
+    }
+    GenericValue timesheet = from('Timesheet').where(parameters).queryOne()
+    // check if party assigned to task, when not add with roletype of project, if assigned check status
+
+    List assigns = from('WorkEffortPartyAssignment')
+            .where(workEffortId: parameters.workEffortId,
+                    partyId: timesheet.partyId)
+            .filterByDate()
+            .queryList()
+
+    if (!assigns) {
+        Map serviceResult = run service: 'getProjectIdAndNameFromTask', with: [taskId: parameters.workEffortId]
+        GenericValue projectAssign = from('WorkEffortPartyAssignment')
+                .where(workEffortId: serviceResult.projectId,
+                        partyId: timesheet.partyId)
+                .queryFirst()
+        if (projectAssign) {
+            run service: 'assignPartyToWorkEffort', with: [*: parameters,
+                                                           partyId:  timesheet.partyId,
+                                                           roleTypeId: projectAssign.roleTypeId,
+                                                           statusId: 'PAS_ASSIGNED']
+        }
+
+        // check if the actual start date is set, when not set it to todays date
+        if (!project.actualStartDate) {
+            run service: 'updateWorkEffort', with: [workEffortId: parameters.workEffortId,
+                                                    actualStartDate: UtilDateTime.nowTimestamp()]
+        }
+    }
+    List timeEntries = timesheet.getRelated('TimeEntry', null, null, false)
+
+    // update existing entries
+    BigDecimal hours = 0
+    timeEntries.findAll {
+        it.workEffortId == parameters.workEffortId &&
+        it.rateTypeId == parameters.rateTypeId
+    }.each {
+        // translate the date into the day number
+        int dayNumber = UtilDateTime.getIntervalInDays(timesheet.fromDate, it.fromDate)
+        hours = parameters["hoursDay${dayNumber}"] != null ? parameters["hoursDay${dayNumber}"] : -1
+        updateTimeEntry(parameters, it, hours, userLogin.partyId, null)
+        parameters["hoursDay${dayNumber}"] = -1
+    }
+
+    // process not yet done fields
+    for (int dayNr = 0; dayNr < 7; dayNr++) {
+        if (parameters["hoursDay${dayNr}"]) {
+            updateTimeEntry(parameters, null, parameters["hoursDay${dayNr}"], userLogin.partyId,
+                    UtilDateTime.addDaysToTimestamp(timesheet.fromDate, dayNr))
+        }
+    }
+
+    // update the assignment status if required
+    if (parameters.checkComplete == 'Y') {
+        GenericValue alreadyAssign = from('WorkEffortPartyAssignment')
+                .where(workEffortId: parameters.workEffortId,
+                        partyId: timesheet.partyId)
+                .filterByDate()
+                .queryFirst()
+        if (alreadyAssign.statusId != 'PAS_COMPLETED') {
+            run service: 'updateTaskAssigment', with: [partyId: timesheet.partyId,
+                                                       statusId: 'PAS_COMPLETED',
+                                                       roleTypeId: alreadyAssign.roleTypeId,
+                                                       fromDate: alreadyAssign.fromDate,
+                                                       workEffortId: parameters.workEffortId]
+        }
+    }
+    return success([timesheetId: timesheet.timesheetId])
+}
+
+/**
+ * Get the projectId when a phase or task is provided.
+ * @return 'Success response resolve project info, failure response otherwise.
+ */
+Map getProjectIdAndNameFromTask() {
+    if (!parameters.taskId && !parameters.phaseId) {
+        return failure(label('ProjectMgrUiLabels', 'ProjectMgrErrorProjectNotFound'))
+    }
+    GenericValue task = null
+    String phaseId = parameters.phaseId
+    if (!phaseId) {
+        task = from('WorkEffort').where(workEffortId: parameters.taskId).queryOne()
+        phaseId = task ? task.workEffortParentId : null
+    }
+    GenericValue phase = from('WorkEffort').where(workEffortId: phaseId).queryOne()
+    if (phase) {
+        GenericValue project = phase.getRelatedOne('ParentWorkEffort', true)
+
+        return success([projectId: project ? project.workEffortId : '',
+                        projectName: project ? project.workEffortName : '',
+                        phaseId: phase ? phase.workEffortId : '',
+                        phaseName: phase ? phase.workEffortName : '',
+                        taskId: task ? task.workEffortId : '',
+                        taskName: task ? task.workEffortName : '',
+                        taskWbsId: project?.workEffortId + '.' + phase?.sequenceNum + '.' + task?.sequenceNum])
+    }
+    return failure(label('ProjectMgrUiLabels', 'ProjectMgrErrorProjectNotFound'))
+}
+
+/**
+ * copy a project with related phases and tasks however no actual data"
+ * @return 'Success response containing the newProjectId in workEffortId and projectId
+ */
+Map copyProject() {
+    GenericValue project = from('WorkEffort').where(workEffortId: parameters.projectId).queryOne()
+    if (!project) {
+        return error(label('ProjectMgrUiLabels', 'ProjectMgrErrorProjectNotFound'))
+    }
+    parameters.workEffortName = parameters.workEffortName ?: project.workEffortName
+    parameters.description = parameters.description ?: project.description
+    parameters.workEffortTypeId = parameters.toTemplate == 'Y' ? 'PROJECT_TEMPLATE' : 'PROJECT'
+    parameters.currentStatusId = 'PRJ_ACTIVE'
+    Map serviceResult = run service: 'createWorkEffort', with: [*: parameters,
+                                                                workEffortId: null]
+    String newProjectId = serviceResult.workEffortId
+
+    // copy assigned parties
+    from('WorkEffortAndPartyAssign')
+            .where(workEffortId: project.workEffortId)
+            .filterByDate()
+            .queryList()
+            .each {
+                run service: 'createWorkEffortPartyAssignment', with: [*: it.getAllFields(),
+                                                                       workEffortId: newProjectId,
+                                                                       fromDate: null,
+                                                                       statusId: 'PAS_ASSIGNED']
+            }
+
+    // copy phase
+    project.getRelated('ChildWorkEffort', null, null, false).each {
+        parameters.workEffortTypeId = parameters.toTemplate ? 'PHASE_TEMPLATE' : 'PHASE'
+        serviceResult = run service: 'createWorkEffort', with: [*: parameters,
+                                                                workEffortName: it.workEffortName,
+                                                                workEffortParentId: newProjectId,
+                                                                currentStatusId: '_NA_',
+                                                                workEffortId: null]
+        String newPhaseId = serviceResult.workEffortId
+        it.getRelated('ChildWorkEffort', null, null, false).each { task ->
+            task.workEffortTypeId = parameters.toTemplate ? 'TASK_TEMPLATE' : 'TASK'
+            task.workEffortParentId = newPhaseId
+            task.currentStatusId = 'PTS_CREATED'
+            run service: 'createWorkEffort', with: [*: task.getAllFields(),
+                                                    workEffortId: null]
+        }
+    }
+    return success([workEffortId: newProjectId, projectId: newProjectId])
+}
+
+/**
+ * get Project information
+ * @return 'Success response containing the projectInfo and projectId
+ */
+Map getProject() {
+    GenericValue project = from('WorkEffort').where(workEffortId: parameters.projectId).cache().queryOne()
+    if (!project) {
+        return success()
+    }
+    Map highInfo = [projectId: project.workEffortId,
+                    projectName: project.workEffortName,
+                    projectDescription: project.description,
+                    parentProjectId: project.workEffortParentId]
+    ['estimatedStartDate', 'estimatedCompletionDate', 'actualStartDate', 'currentStatusId',
+     'actualCompletionDate', 'scopeEnumId', 'createdStamp', 'createdDate'].each {
+        highInfo.(it) = project.(it)
+    }
+
+    //loop through the related phases and tasks
+    highInfo = combineInfo(highInfo, highInfo, parameters.partyId)
+
+    // get e-mail address
+    GenericValue emailAddress = from('WorkEffortContactMechView')
+            .where(workEffortId: project.workEffortId,
+                    contactMechTypeId: 'EMAIL_ADDRESS')
+            .filterByDate()
+            .queryFirst()
+    if (emailAddress) {
+        highInfo.emailAddress = emailAddress.infoString
+    }
+
+    highInfo = createDates(highInfo)
+
+    return success([projectInfo: highInfo,
+                    projectId: project.workEffortId])
+}
+
+/**
+ * get Project Phase information
+ * @return 'Success response containing the phaseList and projectId
+ */
+Map getProjectPhaseList() {
+    GenericValue project = from('WorkEffort').where(workEffortId: parameters.projectId).cache().queryOne()
+    if (!project) {
+        return error(label('ProjectMgrUiLabels', 'ProjectMgrErrorProjectNotFound'))
+    }
+
+    List phaseList = []
+    from('WorkEffort')
+            .where(workEffortTypeId: 'PHASE', workEffortParentId: parameters.projectId)
+            .orderBy('sequenceNum', 'workEffortName')
+            .queryList()
+            .each {
+                Map highInfo = [sequenceNum: it.sequenceNum,
+                                phaseId: it.workEffortId,
+                                phaseSeqNum: it.sequenceNum,
+                                phaseName: it.workEffortName,
+                                phaseDescription: it.description,
+                                scopeEnumId: it.scopeEnumId]
+                highInfo = combineInfo(highInfo, it, parameters.partyId)
+                highInfo = createDates(highInfo)
+                phaseList << highInfo
+            }
+    return success([phaseList: phaseList, projectId: parameters.projectId])
+}
+
+/**
+ * get Project Phase/task information
+ * @return 'Success response containing the taskList and projectId
+ */
+Map getProjectTaskList() {
+    GenericValue project = from('WorkEffort').where(workEffortId: parameters.projectId).cache().queryOne()
+    if (!project) {
+        return error(label('ProjectMgrUiLabels', 'ProjectMgrErrorProjectNotFound'))
+    }
+
+    List taskList = []
+    from('ProjectAndPhaseAndTask')
+            .where(workEffortTypeId: 'PHASE', workEffortParentId: parameters.projectId)
+            .orderBy('phaseName', 'phaseSeqNum', 'sequenceNum', 'workEffortName')
+            .queryList()
+            .each {
+                Map highInfo = [phaseName: it.phaseName,
+                        phaseSeqNum: it.phaseSeqNum,
+                        taskId: it.workEffortId]
+                highInfo = combineInfo(highInfo, it, parameters.partyId)
+                highInfo = createDates(highInfo)
+                highInfo.workEffortId = it.workEffortId
+                highInfo.workEffortName = it.workEffortName
+                highInfo.sequenceNum = it.sequenceNum
+                taskList << highInfo
+            }
+    return success([taskList: taskList, projectId: parameters.projectId])
+}
+
+/**
+ * Resolve information on task
+ * @return 'Success response containing the taskInfo
+ */
+Map getProjectTask() {
+    GenericValue task = from('WorkEffort').where(workEffortId: parameters.taskId).cache().queryOne()
+    Map highInfo = [taskId: task.workEffortId,
+            taskSeqNum: task.sequenceNum,
+            taskName: task.workEffortName,
+            taskDescription: task.description,
+            scopeEnumId: task.scopeEnumId,
+            workEffortParentId: task.workEffortParentId]
+    return success([taskInfo: combineInfo(highInfo, task, parameters.partyId)])
+}
+
+/**
+ * get Project information by party member
+ * @return 'Success response containing the projectParties
+ */
+Map getProjectsByParties() {
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        if (parameters.projectId) {
+            EQUALS(projectId: parameters.projectId)
+        }
+        if (parameters.partyId) {
+            EQUALS(partyId: parameters.partyId)
+        } else {
+            NOT_EQUAL(partyId: null)
+        }
+    }
+    List tasks = from('ProjectAndPhaseAndTaskParty')
+            .where(condition)
+            .orderBy('projectId', 'partyId')
+            .queryList()
+    List projectParties = []
+    Map projectParty = [:]
+    for (GenericValue task: tasks) {
+        if (projectParty && task.partyId != projectParty.partyId) {
+            projectParties << projectParty
+            projectParty = [:]
+        }
+        if (!projectParty) {
+            projectParty.partyId = task.partyId
+            GenericValue partyNameView = from('PartyNameView').where(partyId: task.partyId).cache().queryOne()
+            if (partyNameView) {
+                projectParty.partyName = "${partyNameView.lastName ?: ''},${partyNameView.firstName ?: ''}${partyNameView.groupName ?: ''}"
+            }
+            projectParty.roleTypeId = task.roleTypeId
+            projectParty.fromDate = task.fromDate
+            projectParty.thruDate = task.thruDate
+        }
+        // get the planned/actual hours
+        projectParty.putAll(getHours(task, task, task.partyId))
+        if (!projectParty) {
+            projectParties << projectParty
+        }
+    }
+
+    return success([projectParties: projectParties])
+}
+
+/**
+ * get task information by party member
+ * @return 'Success response containing the taskParties
+ */
+Map getTasksByParties() {
+    // get the list of tasks optionaly selected for a party
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        if (parameters.partyId) {
+            EQUALS(partyId: parameters.partyId)
+        }
+        if (parameters.workEffortId) {
+            EQUALS(workEffortId: parameters.workEffortId)
+        }
+    }
+    Map taskParty = [:]
+    List taskParties = []
+    from('WorkEffortPartyAssignment')
+            .where(condition)
+            .filterByDate()
+            .orderBy('workEffortId', 'partyId')
+            .queryList()
+            .each {
+                if (taskParty && taskParty.partyId != it.partyId) {
+                    taskParties << taskParty
+                    taskParty = [:]
+                }
+                if (!taskParty) {
+                    taskParty.partyId = it.partyId
+                    GenericValue partyNameView = from('PartyNameView').where(partyId: it.partyId).cache().queryOne()
+                    if (partyNameView) {
+                        taskParty.partyName = "${partyNameView.lastName ?: ''},${partyNameView.firstName ?: ''}${partyNameView.groupName ?: ''}"
+                    }
+                    taskParty.roleTypeId = it.roleTypeId
+                    taskParty.statusId = it.statusId
+                    taskParty.fromDate = it.fromDate
+                    taskParty.thruDate = it.thruDate
+                }
+
+                // get the planned hours
+                taskParty.putAll(getHours(taskParty, it.getRelatedOne('WorkEffort', true), it.partyId))
+            }
+    if (taskParties) {
+        taskParties << taskParty
+    }
+    return success([taskParties: taskParties])
+}
+
+/**
+ * Creates TimeEntry and searches for a timesheetId if not provided
+ * @return 'Success response containing the timesheetId and fromDate, error response otherwise.
+ */
+Map createTimeEntryInTimesheet() {
+    String timesheetId = parameters.timesheetId
+    if (parameters.fromDate && !parameters.timesheetId) {
+        GenericValue timesheet = from('Timesheet').where(partyId: parameters.partyId).filterByDate().queryFirst()
+        if (timesheet) {
+            if (timesheet.statusId != 'TIMESHEET_IN_PROCESS') {
+                return error(label('ProjectMgrUiLabels', 'ProjectMgrCannotAddToTimesheet'))
+            }
+            timesheetId = timesheet.timesheetId
+        } else {
+            // create new timesheet
+            Map serviceReturn = run service: 'createTimesheetForThisWeek', with: [*: parameters,
+                                                                                  requiredDate: parameters.fromDate]
+            timesheetId = serviceReturn.timesheetId
+        }
+    }
+    // get role for this party in this project
+    if (parameters.roleTypeId) {
+        GenericValue taskRole = from('ProjectPartyAndPhaseAndTask')
+                .where(partyId: parameters.partyId,
+                        workEffortId: parameters.workEffortId)
+                .queryFirst()
+        run service: 'assignPartyToWorkEffort', with: [*: taskRole.getAllFields(),
+                                                       statusId: 'PAS_ASSIGNED']
+    }
+    Map serviceResult = run service: 'createTimeEntry', with: [*: parameters,
+                                                               timesheetId: timesheetId]
+    return success([fromDate: serviceResult.fromDate, timesheetId: timesheetId])
+}
+
+/**
+ * Add all reported time on all completed timeSheets from all workEfforts for a project
+ * @return 'Success response containing the invoiceId, error response otherwise.
+ */
+Map addProjectTimeToInvoice() {
+    // Recreate the invoice if still in preparation in order to correct errors.
+    boolean createInvoice = !parameters.reCreate
+    if (parameters.reCreate == 'Y') {
+        GenericValue invoice = from('Invoice').where(parameters).queryOne()
+        if (!invoice) {
+            return error(label('WorkEffortUiLabels', 'WorkEffortTimesheetCannotFindInvoice'))
+        }
+        //FIXME <call-simple-method method-name="checkInvoiceStatusInProgress"
+        // xml-resource="component://accounting/minilang/invoice/InvoiceServices.xml"/>
+        EntityCondition removeCond = EntityCondition.makeCondition('invoiceId', parameters.invoiceId)
+        delegator.storeByCondition('TimeEntry',
+                [invoiceId: null, invoiceItemSeqId: null],
+                removeCond)
+        delegator.removeByCondition('InvoiceItem', removeCond)
+        createInvoice = true //do not create, only add
+    }
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        EQUALS(projectId: parameters.projectId)
+        EQUALS(invoiceId: null)
+        EQUALS(timesheetStatusId: 'TIMESHEET_COMPLETED')
+        if (parameters.thruDate) {
+            LESS_THAN(fromDate: parameters.thruDate)
+        }
+    }
+    List tasks = from('ProjectPhaseTaskAndTimeEntryTimeSheet')
+            .where(condition)
+            .orderBy('workEffortId')
+            .queryList()
+    if (!tasks) {
+        return error(label('WorkEffortUiLabels', 'ProjectMgrNoTimeentryItemsFound'))
+    }
+    String invoiceId = parameters.invoiceId
+    if (createInvoice) {
+        Map serviceResult = run service: 'addWorkEffortTimeToNewInvoice', with: [*: parameters,
+                                                                                 workEffortId: parameters.projectId,
+                                                                                 combineInvoiceItem: 'Y']
+        invoiceId = serviceResult.invoiceId
+    }
+    tasks*.workEffortId.unique().each {
+        run service: 'addWorkEffortTimeToInvoice', with: [*: parameters,
+                                                          invoiceId: invoiceId,
+                                                          combineInvoiceItem: 'Y',
+                                                          taskId: it]
+    }
+    return success([invoiceId: invoiceId])
+}
+
+/**
+ * SECA to add either project-testing or -approval parties to a task when a task is set to complete
+ * @return 'Success response after adding, error response otherwise.
+ */
+Map addValidationPartiesToTask() {
+    // check if this is the last party which completed his task
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        EQUALS(workEffortId: parameters.workEffortId)
+        NOT_EQUAL(statusId: 'PAS_COMPLETED')
+        NOT_EQUAL(partyId: parameters.partyId)
+    }
+    if (from('addValidationPartiesToTask')
+            .where(condition)
+            .queryCount() == 0) {
+        Map serviceResult = run service: 'getProjectIdAndNameFromTask', with: [taskId: parameters.workEffortId]
+        String projectId = serviceResult.projectId
+
+        // see who is responsible for testing/validation in this project
+        condition = new EntityConditionBuilder().AND {
+            EQUALS(workEffortId: projectId)
+            NOT_EQUAL(partyId: parameters.partyId)
+            IN(roleTypeId: ['PROVIDER_VALIDATOR', 'PROVIDER_TESTER'])
+        }
+        List assigns = from('WorkEffortPartyAssignment')
+                .where(condition)
+                .filterByDate()
+                .queryList()
+        if (assigns) {
+            assigns.each {
+                run service: 'createWorkEffortPartyAssignment', with: [*: it.getAllFields(),
+                                                                       workEffortId: parameters.workEffortId,
+                                                                       statusId: 'PAS_ASSIGNED']
+            }
+        } else {
+            logInfo('No validation parties defined in this project: no validation parties added....')
+        }
+    } else {
+        logInfo('Not the last party who completes his task: validation parties not added....')
+    }
+    return success()
+}
+
+// Internal functions
+/**
+ * combine lower level Actual hours info.
+ * @return Map highInfo with actual hours
+ */
+private Map combineActualHours(Map highInfo, String partyId) {
+    /* to calculate actual hours : the declared number of hours in time entry should be multiplied by the
+     *    max percentage declared in PartyRate if a valid party rate can be found for the party associated to a
+     *    the timesheet associated to this time entry and has the same rateType as this timeEntry
+     * actualHoursOriginal is the total of hours in time entries without application of percentage declared in partyRate
+     */
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        if (highInfo.projectId) {
+            EQUALS(projectId: highInfo.projectId)
+        }
+        if (highInfo.phaseId) {
+            EQUALS(phaseId: highInfo.phaseId)
+        }
+        if (highInfo.taskId) {
+            EQUALS(taskId: highInfo.taskId)
+        }
+        if (partyId) {
+            EQUALS(hoursPartyId: partyId)
+        }
+    }
+    Map notRatedValue = from('ProjectPhaseTaskActualNotRatedHoursView')
+            .where(condition)
+            .select('totalOriginalHours')
+            .queryFirst()
+    BigDecimal originalHours = notRatedValue.totalOriginalHours
+
+    /* II- get total for timeEntries having a partyRate that should be applied
+       before applying rate (totalOriginalHours)
+       after applying rate (totalRatedHours)
+     */
+    Map ratedValue = from('ProjectPhaseTaskActualRatedHoursView')
+            .where(condition)
+            .select('totalOriginalHours', 'totalRatedHours')
+            .queryFirst()
+    // not used ratedValue.totalRatedHours  because not works, reason seem to be totalRatedHours is a calculated field ???
+    highInfo.actualHours = (ratedValue.totalRatedHours ?: 0) + (originalHours ?: 0)
+    highInfo.originalActualHours = originalHours ?: 0 + (ratedValue.totalOriginalHours ?: 0)
+
+    // do the same but for non-billed hours
+    // first get not rated hours
+    condition = new EntityConditionBuilder().AND {
+        if (highInfo.projectId) {
+            EQUALS(projectId: highInfo.projectId)
+        }
+        if (highInfo.phaseId) {
+            EQUALS(phaseId: highInfo.phaseId)
+        }
+        if (highInfo.taskId) {
+            EQUALS(taskId: highInfo.taskId)
+        }
+        if (partyId) {
+            EQUALS(hoursPartyId: partyId)
+        }
+        EQUALS(invoiceId: null)
+    }
+    notRatedValue = from('ProjectPhaseTaskActualNotRatedHoursView')
+            .where(condition)
+            .select('totalOriginalHours')
+            .queryFirst()
+    BigDecimal actualNonBilledHours = 0
+    if (notRatedValue.totalOriginalHours) {
+        // second get non billed for entries having an invoiceId
+        ratedValue = from('ProjectPhaseTaskActualRatedHoursView')
+                .where(condition)
+                .select('totalOriginalHours', 'totalRatedHours')
+                .queryFirst()
+        actualNonBilledHours = notRatedValue.totalOriginalHours + (ratedValue.totalOriginalHours ?: 0)
+    }
+    highInfo.actualNonBilledHours = actualNonBilledHours
+    return highInfo
+}
+
+/**
+ * combine lower level status, dates of tasks.
+ * @return Map highInfo
+ */
+private Map combineInfo(Map highInfo, Map lowInfo, String partyId) {
+    highInfo.currentStatusId = combineStatusInfo(highInfo, lowInfo)
+    highInfo = combineDatesAndPlannedHoursInfo(highInfo)
+    return combineActualHours(highInfo, partyId)
+}
+
+/**
+ * Merge the estimated and actual dates
+ * @return Map highInfo
+ */
+private Map createDates(Map highInfo) {
+    // input/output is 'highInfo map
+    // create dates taking the last known one to save space on the list
+    highInfo.startDate = highInfo.actualStartDate ?: highInfo.estimatedStartDate
+    highInfo.completionDate = highInfo.actualCompletionDate ?: highInfo.estimatedCompletionDate
+    return highInfo
+}
+
+/**
+ * Get the planned and estimated hours for a task and add to the highInfo map
+ * @return Map highInfo with hours
+ */
+private Map getHours(Map highInfo, Map lowInfo, String partyId) {
+    // input is 'lowInfo' map output is 'highInfo' map
+    // PartyId: if provided only the hours of that party
+    // add the planned hours together
+    from('WorkEffortSkillStandard')
+            .where(workEffortId: lowInfo.workEffortId)
+            .queryList()
+            .each {
+                if (it.estimatedDuration) {
+                    highInfo.plannedHours = (highInfo.plannedHours ?: 0) + it.estimatedDuration
+                }
+            }
+
+    // get the actual billed / non billed hours
+    from('TimeEntry')
+            .where(workEffortId: lowInfo.workEffortId)
+            .queryList()
+            .each {
+                if (it.hours) {
+                    GenericValue timesheet = it.getRelatedOne('Timesheet', true)
+
+                    // check if only a part of the registered hours need to be taken into account
+                    BigDecimal originalActualHours = it.hours
+                    GenericValue partyRate = from('PartyRate')
+                            .where(partyId: timesheet.partyId,
+                                    rateTypeId: it.rateTypeId)
+                            .filterByDate(it.fromDate)
+                            .queryFirst()
+                    if (partyRate.percentageUsed) {
+                        it.actualHours = (it.actualHours * partyRate.percentageUsed) / 100
+                    }
+                    if (partyId && timesheet.partyId == partyId) {
+                        highInfo.originalActualHours = originalActualHours + (highInfo.originalActualHours ?: 0)
+                        highInfo.actualHours = it.actualHours + (highInfo.actualHours ?: 0)
+                        if (!it.invoiceId) {
+                            highInfo.actualNonBilledHours = it.hours + (highInfo.actualNonBilledHours ?: 0)
+                        }
+                    }
+
+                    // keep also a general total for the actual hours of all participants
+                    highInfo.actualTotalHours = it.hours + highInfo.actualTotalHours ?: 0
+                    if (!it.invoiceId) {
+                        highInfo.actualNonBilledTotalHours = it.hours + (highInfo.actualNonBilledTotalHours ?: 0)
+                    }
+                }
+            }
+    return highInfo
+}
+
+/**
+ * update a timeEntry in silence
+ * @return nothing
+ */
+private void updateTimeEntry(Map parameters, GenericValue timeEntry, BigDecimal hours, String partyId, Timestamp fromDate) {
+    if (hours == -1 ) {
+        return
+    }
+    if (timeEntry && timeEntry.timeEntryId) {
+        if (hours == 0) {
+            run service: 'deleteTimeEntry', with: [timeEntryId: timeEntry.timeEntryId]
+        } else {
+            run service: 'updateTimeEntry', with: [hours: hours,
+                                                   timeEntryId: timeEntry.timeEntryId,
+                                                   rateTypeId: timeEntry.rateTypeId,
+                                                   partyId: timeEntry.partyId]
+        }
+    } else {
+        run service: 'createTimeEntry', with: [*: parameters,
+                                               hours: hours,
+                                               fromDate: fromDate,
+                                               partyId: partyId]
+    }
+}
+
+/**
+ * combine lower level status
+ * The status for a project or phase is
+ * * IN_PROGRESS if at least one task still in progress
+ * * COMPLETED if all task are either completed or cancelled
+ * * CREATED if other conditions does not apply
+ * For a task the status is
+ * * IN_PROGRESS if it has at least one resource and at least a time entry
+ * * ASSIGNED if it has at least one resource but no time entry associated
+ * @return statusId after analyse
+ */
+private String combineStatusInfo(Map highInfo, Map lowInfo = null) {
+    if (lowInfo && lowInfo.workEffortTypeId == 'TASK') {
+        if (lowInfo.currentStatusId == 'PTS_CREATED' &&
+                from('WorkEffortPartyAssignment')
+                        .where(workEffortId: lowInfo.workEffortId)
+                        .filterByDate()
+                        .queryCount() > 0) {
+            return (from('TimeEntry')
+                    .where(workEffortId: lowInfo.workEffortId)
+                    .queryCount() > 0) ? 'PTS_CREATED_AS' : 'PTS_CREATED_IP'
+        }
+        return lowInfo.currentStatusId
+    }
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        if (highInfo.projectId) {
+            EQUALS(projectId: highInfo.projectId)
+        }
+        if (highInfo.phaseId) {
+            EQUALS(phaseId: highInfo.phaseId)
+        }
+        if (highInfo.taskId) {
+            EQUALS(taskId: highInfo.taskId)
+        }
+    }
+    long tasksCount = from('ProjectPhaseTaskAssignmentView').where(condition).queryCount()
+    EntityCondition completedCond = new EntityConditionBuilder().AND {
+        IN(taskStatusId: ['PTS_CANCELLED', 'PTS_COMPLETED'])
+    }
+    long completedTasks = from('ProjectPhaseTaskAssignmentView').where(condition).having(completedCond).queryCount()
+    if (completedTasks == tasksCount) {
+        return 'PTS_COMPLETED'
+    }
+
+    EntityCondition assignedCond = new EntityConditionBuilder().AND {
+        EQUALS(entriesCount: 0L)
+        GREATER_THAN_EQUAL_TO(resourceCount: 1L)
+        NOT_IN(taskStatusId: ['PTS_CANCELLED', 'PTS_COMPLETED'])
+    }
+    long assignedTasks = from('ProjectPhaseTaskAssignmentView').where(condition).having(assignedCond).queryCount()
+    EntityCondition relatedCond = new EntityConditionBuilder().AND {
+        GREATER_THAN_EQUAL_TO(entriesCount: 1L)
+        GREATER_THAN_EQUAL_TO(resourceCount: 1L)
+        NOT_IN(taskStatusId: ['PTS_CANCELLED', 'PTS_COMPLETED'])
+    }
+    long progressTasks = from('ProjectPhaseTaskAssignmentView').where(condition).having(relatedCond).queryCount()
+    return (progressTasks > 0 || assignedTasks > 0) ? 'PTS_CREATED_IP' : 'PTS_CREATED'
+}
+
+/**
+ * combine lower level start end dates and planned hours for a project, phase or task
+ * @return Map containning date info
+ */
+private Map combineDatesAndPlannedHoursInfo(Map highInfo) {
+    EntityCondition condition = new EntityConditionBuilder().AND {
+        if (highInfo.projectId) {
+            EQUALS(projectId: highInfo.projectId)
+        }
+        if (highInfo.phaseId) {
+            EQUALS(phaseId: highInfo.phaseId)
+        }
+        if (highInfo.taskId) {
+            EQUALS(taskId: highInfo.taskId)
+        }
+    }
+
+    // Now used TimeEntries to update (or not) actual start and end Date
+    Map summaryInfo = from('ProjectPhaseTaskSklSumView')
+            .where(condition)
+            .select('projectId', 'estimatedStartDate', 'actualStartDate',
+                    'estimatedCompletionDate', 'actualCompletionDate', 'plannedHours', 'priority')
+            .queryFirst()
+    if (summaryInfo) {
+        highInfo.putAll(summaryInfo.getAllFields())
+    }
+
+    // update actual start date by the min date form sub tasks associated TimeEntries
+    // (if before actualStartDate field)
+    Map timeEntriesInfo = from('ProjectPhaseTaskActualEntrySumView')
+            .where(condition)
+            .select('actualEntryStartDate')
+            .queryFirst()
+    if (timeEntriesInfo && highInfo.actualStartDate > timeEntriesInfo.actualEntryStartDate) {
+        highInfo.actualStartDate = timeEntriesInfo.actualEntryStartDate
+    }
+    return highInfo
+}
diff --git a/projectmgr/webapp/projectmgr/WEB-INF/controller.xml b/projectmgr/webapp/projectmgr/WEB-INF/controller.xml
index 2b332c6..1858c7d 100644
--- a/projectmgr/webapp/projectmgr/WEB-INF/controller.xml
+++ b/projectmgr/webapp/projectmgr/WEB-INF/controller.xml
@@ -372,13 +372,13 @@
     </request-map>
     <request-map uri="updateMyTimesheet">
         <security https="true" auth="true"/>
-        <event type="service-multi" invoke="updateTimeEntryByWorkeffort"/>
+        <event type="service-multi" invoke="updateTimeEntryByWorkEffort"/>
         <response name="success" type="view-home" value="MyTimesheet"/>
         <response name="error" type="view-home" value="MyTimesheet"/>
     </request-map>
     <request-map uri="updateTimesheet">
         <security https="true" auth="true"/>
-        <event type="service-multi" invoke="updateTimeEntryByWorkeffort"/>
+        <event type="service-multi" invoke="updateTimeEntryByWorkEffort"/>
         <response name="success" type="view" value="Timesheet"/>
         <response name="error" type="view" value="Timesheet"/>
     </request-map>