This is an automated email from the ASF dual-hosted git repository.

mridulpathak pushed a commit to branch release24.09
in repository https://gitbox.apache.org/repos/asf/ofbiz-plugins.git


The following commit(s) were added to refs/heads/release24.09 by this push:
     new 1ac2e6050 Fixed: Multiple regressions in the minilang-to-Groovy 
conversion of ProjectServicesScript.groovy (OFBIZ-13607)
1ac2e6050 is described below

commit 1ac2e605097a47582f97e40ed4b5c9519d627f70
Author: Mridul Pathak <[email protected]>
AuthorDate: Sun Sep 20 10:37:29 2026 +0530

    Fixed: Multiple regressions in the minilang-to-Groovy conversion of 
ProjectServicesScript.groovy (OFBIZ-13607)
    
    - createProject()/updateProject() no longer fire the work-effort e-mail 
address services asynchronously, which silently swallowed validation/creation 
errors and deferred persistence past the caller's transaction.
    - updateTaskAssigment() now preserves 
delegateReasonEnumId/comments/assignedByUserLoginId on the assignment record it 
closes, clears the PAS_ENDED sentinel before persisting (it is not a real 
StatusItem and violates the statusId FK otherwise), and persists a 
PAS_COMPLETED status update before handing off to updateTaskStatusToComplete() 
instead of returning early and skipping the persist.
    - updateTaskStatusToComplete() compared a queryOne() GenericValue to 0, 
which is never true; switched to queryCount() so the completion cascade is 
reachable at all.
    - getProjectsByParties()/getTasksByParties() fixed the post-loop flush of 
the last accumulated party group, which was previously dropped or, for a 
single-party result, returned empty.
    - createTimeEntryInTimesheet() fixed the inverted if (roleTypeId) condition 
that made the auto role lookup/assignment run only when it wasn't needed (and 
NPE when it was), and fixed a separate bug where the service never returned 
timeEntryId despite the servicedef requiring it as OUT, so the service failed 
validation on every call.
    - addProjectTimeToInvoice()/addProjectTimeToNewInvoice() fixed the inverted 
createInvoice flag so addProjectTimeToNewInvoice reuses/repopulates an existing 
invoice on reCreate='Y' instead of always minting a new one, and so 
addProjectTimeToInvoice (add to an existing invoice) never creates a new one as 
its own description promises; restored the two services' independent reCreate 
defaults in servicedef to match their original (pre-conversion) behavior.
    - addValidationPartiesToTask() queried a nonexistent 
"addValidationPartiesToTask" entity instead of WorkEffortPartyAssignment, so 
this SECA threw every time a task assignment was completed.
    - combineActualHours() fixed an Elvis/plus operator-precedence bug that 
silently dropped the rated-hours portion whenever the unrated hours were 
non-zero.
    - getHours() fixed a write to a nonexistent actualHours field on the 
TimeEntry entity (it only has hours), which threw whenever a matching PartyRate 
applied.
---
 projectmgr/servicedef/services.xml                 |  3 +-
 .../ofbiz/projectmgr/ProjectServicesScript.groovy  | 53 ++++++++++++++--------
 2 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/projectmgr/servicedef/services.xml 
b/projectmgr/servicedef/services.xml
index 1c55aedb8..6a5dd5d79 100644
--- a/projectmgr/servicedef/services.xml
+++ b/projectmgr/servicedef/services.xml
@@ -269,7 +269,7 @@ under the License.
         <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" default-value="Y"/>
+        <attribute name="reCreate" type="String" mode="IN" optional="true"/>
     </service>
     <service name="addProjectTimeToInvoice" engine="groovy" auth="true"
         
location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy"
 invoke="addProjectTimeToInvoice">
@@ -278,6 +278,7 @@ under the License.
         <attribute name="invoiceId" type="String" mode="IN"/>
         <attribute name="billedPartyId" type="String" mode="IN"/>
         <attribute name="thruDate" type="Timestamp" mode="IN" optional="true"/>
+        <attribute name="reCreate" type="String" mode="IN" optional="true" 
default-value="N"/>
     </service>
     <service name="addValidationPartiesToTask" engine="groovy" auth="true"
         
location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy"
 invoke="addValidationPartiesToTask">
diff --git 
a/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy
 
b/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy
index a1099ab7e..330a8c652 100644
--- 
a/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy
+++ 
b/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/ProjectServicesScript.groovy
@@ -183,7 +183,13 @@ Map updateTaskAssigment() {
     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
+        GenericValue oldAssignmentPk = makeValue('WorkEffortPartyAssignment')
+        oldAssignmentPk.setPKFields(parameters)
+        update('WorkEffortPartyAssignment').where(oldAssignmentPk).set([
+                thruDate: UtilDateTime.nowTimestamp(),
+                delegateReasonEnumId: parameters.delegateReasonEnumId,
+                comments: parameters.comments,
+                assignedByUserLoginId: userLogin.userLoginId])
         Map serviceResult = run service: 'createWorkEffortPartyAssignment', 
with: [*: parameters,
                                                                                
    partyId: parameters.newPartyId,
                                                                                
    statusId: 'PAS_ASSIGNED',
@@ -191,16 +197,19 @@ Map updateTaskAssigment() {
         fromDate = serviceResult.fromDate
     } else {
         GenericValue partyAssignment = 
from('WorkEffortAndPartyAssign').where(parameters).queryOne()
+        boolean markComplete = false
         if (partyAssignment) {
             if (parameters.statusId == 'PAS_ENDED') {
                 run service: 'expireWorkEffortPartyAssignment', with: 
parameters
+                parameters.statusId = null
             }
-            if (parameters.statusId == 'PAS_COMPLETED') {
-                return updateTaskStatusToComplete(parameters.workEffortId)
-            }
+            markComplete = parameters.statusId == 'PAS_COMPLETED'
         }
         String serviceName = partyAssignment ? 
'updateWorkEffortPartyAssignment' : 'assignPartyToWorkEffort'
         run service: serviceName, with: parameters
+        if (markComplete) {
+            return updateTaskStatusToComplete(parameters.workEffortId)
+        }
     }
     return success([workEffortId: parameters.workEffortId, fromDate: fromDate])
 }
@@ -217,7 +226,7 @@ Map updateTaskStatusToComplete(String workEffortId) {
     boolean canComplete = from('WorkEffortPartyAssignment')
             .where(condition)
             .filterByDate()
-            .queryOne() == 0
+            .queryCount() == 0
     if (canComplete) {
         run service: 'updateWorkEffort', with: [
                 workEffortId: workEffortId,
@@ -643,9 +652,9 @@ Map getProjectsByParties() {
         }
         // get the planned/actual hours
         projectParty.putAll(getHours(task, task, task.partyId))
-        if (!projectParty) {
-            projectParties << projectParty
-        }
+    }
+    if (projectParty) {
+        projectParties << projectParty
     }
 
     return success([projectParties: projectParties])
@@ -692,7 +701,7 @@ Map getTasksByParties() {
                 // get the planned hours
                 taskParty.putAll(getHours(taskParty, 
it.getRelatedOne('WorkEffort', true), it.partyId))
             }
-    if (taskParties) {
+    if (taskParty) {
         taskParties << taskParty
     }
     return success([taskParties: taskParties])
@@ -719,17 +728,21 @@ Map createTimeEntryInTimesheet() {
         }
     }
     // get role for this party in this project
-    if (parameters.roleTypeId) {
+    if (!parameters.roleTypeId) {
         GenericValue taskRole = from('ProjectPartyAndPhaseAndTask')
                 .where(partyId: parameters.partyId,
                         workEffortId: parameters.workEffortId)
+                .filterByDate()
                 .queryFirst()
-        run service: 'assignPartyToWorkEffort', with: [*: 
taskRole.getAllFields(),
-                                                       statusId: 
'PAS_ASSIGNED']
+        if (taskRole) {
+            parameters.roleTypeId = taskRole.roleTypeId
+            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])
+    return success([timeEntryId: serviceResult.timeEntryId, fromDate: 
serviceResult.fromDate, timesheetId: timesheetId])
 }
 
 /**
@@ -751,7 +764,7 @@ Map addProjectTimeToInvoice() {
                 [invoiceId: null, invoiceItemSeqId: null],
                 removeCond)
         delegator.removeByCondition('InvoiceItem', removeCond)
-        createInvoice = true //do not create, only add
+        createInvoice = false //do not create, only add
     }
     EntityCondition condition = new EntityConditionBuilder().AND {
         EQUALS(projectId: parameters.projectId)
@@ -795,8 +808,9 @@ Map addValidationPartiesToTask() {
         NOT_EQUAL(statusId: 'PAS_COMPLETED')
         NOT_EQUAL(partyId: parameters.partyId)
     }
-    if (from('addValidationPartiesToTask')
+    if (from('WorkEffortPartyAssignment')
             .where(condition)
+            .filterByDate()
             .queryCount() == 0) {
         Map serviceResult = run service: 'getProjectIdAndNameFromTask', with: 
[taskId: parameters.workEffortId]
         String projectId = serviceResult.projectId
@@ -867,7 +881,7 @@ private Map combineActualHours(Map highInfo, String 
partyId) {
             .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)
+    highInfo.originalActualHours = (originalHours ?: 0) + 
(ratedValue.totalOriginalHours ?: 0)
 
     // do the same but for non-billed hours
     // first get not rated hours
@@ -952,17 +966,18 @@ private Map getHours(Map highInfo, Map lowInfo, String 
partyId) {
 
                     // check if only a part of the registered hours need to be 
taken into account
                     BigDecimal originalActualHours = it.hours
+                    BigDecimal actualHours = 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 (partyRate?.percentageUsed) {
+                        actualHours = (actualHours * partyRate.percentageUsed) 
/ 100
                     }
                     if (partyId && timesheet.partyId == partyId) {
                         highInfo.originalActualHours = originalActualHours + 
(highInfo.originalActualHours ?: 0)
-                        highInfo.actualHours = it.actualHours + 
(highInfo.actualHours ?: 0)
+                        highInfo.actualHours = actualHours + 
(highInfo.actualHours ?: 0)
                         if (!it.invoiceId) {
                             highInfo.actualNonBilledHours = it.hours + 
(highInfo.actualNonBilledHours ?: 0)
                         }

Reply via email to