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)
}