001// -------------------------------------------------------------------------------- 002// Copyright 2002-2025 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.model.control.workrequirement.server.logic; 018 019import com.echothree.model.control.core.server.control.EntityInstanceControl; 020import com.echothree.model.control.sequence.common.SequenceTypes; 021import com.echothree.model.control.sequence.server.control.SequenceControl; 022import com.echothree.model.control.sequence.server.logic.SequenceGeneratorLogic; 023import com.echothree.model.control.workflow.server.control.WorkflowControl; 024import com.echothree.model.control.workflow.server.logic.WorkflowDestinationLogic; 025import com.echothree.model.control.workrequirement.common.workflow.WorkAssignmentStatusConstants; 026import com.echothree.model.control.workrequirement.common.workflow.WorkRequirementStatusConstants; 027import com.echothree.model.control.workrequirement.common.workflow.WorkTimeStatusConstants; 028import com.echothree.model.control.workrequirement.server.control.WorkRequirementControl; 029import com.echothree.model.data.party.common.pk.PartyPK; 030import com.echothree.model.data.party.server.entity.Party; 031import com.echothree.model.data.user.server.entity.UserVisit; 032import com.echothree.model.data.workeffort.server.entity.WorkEffort; 033import com.echothree.model.data.workrequirement.server.entity.WorkAssignment; 034import com.echothree.model.data.workrequirement.server.entity.WorkRequirement; 035import com.echothree.model.data.workrequirement.server.entity.WorkRequirementScope; 036import com.echothree.model.data.workrequirement.server.entity.WorkTime; 037import com.echothree.util.common.persistence.BasePK; 038import com.echothree.util.server.persistence.Session; 039import javax.enterprise.context.ApplicationScoped; 040import javax.enterprise.inject.spi.CDI; 041 042@ApplicationScoped 043public class WorkRequirementLogic { 044 045 protected WorkRequirementLogic() { 046 super(); 047 } 048 049 public static WorkRequirementLogic getInstance() { 050 return CDI.current().select(WorkRequirementLogic.class).get(); 051 } 052 053 public WorkRequirement createWorkRequirementUsingNames(final Session session, final WorkEffort workEffort, final String workRequirementTypeName, 054 final Party assignedParty, final Long assignedEndTime, final Long requiredTime, final BasePK createdBy) { 055 var workRequirementControl = Session.getModelController(WorkRequirementControl.class); 056 var workEffortScope = workEffort.getLastDetail().getWorkEffortScope(); 057 var workEffortType = workEffortScope.getLastDetail().getWorkEffortType(); 058 var workRequirementType = workRequirementControl.getWorkRequirementTypeByName(workEffortType, workRequirementTypeName); 059 var workRequirementScope = workRequirementControl.getWorkRequirementScope(workEffortScope, workRequirementType); 060 061 return createWorkRequirement(session, workEffort, workRequirementScope, assignedParty, assignedEndTime, requiredTime, createdBy); 062 } 063 064 public WorkRequirement createWorkRequirement(final Session session, final WorkEffort workEffort, final WorkRequirementScope workRequirementScope, 065 final Party assignedParty, final Long assignedEndTime, Long requiredTime, final BasePK createdBy) { 066 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 067 var workflowControl = Session.getModelController(WorkflowControl.class); 068 var workRequirementControl = Session.getModelController(WorkRequirementControl.class); 069 var sequenceControl = Session.getModelController(SequenceControl.class); 070 var workRequirementScopeDetail = workRequirementScope.getLastDetail(); 071 var workRequirementTypeDetail = workRequirementScopeDetail.getWorkRequirementType().getLastDetail(); 072 var workRequirementSequence = workRequirementScopeDetail.getWorkRequirementSequence(); 073 074 if(workRequirementSequence == null) { 075 workRequirementSequence = workRequirementTypeDetail.getWorkRequirementSequence(); 076 077 if(workRequirementSequence == null) { 078 workRequirementSequence = sequenceControl.getDefaultSequenceUsingNames(SequenceTypes.WORK_REQUIREMENT.name()); 079 } 080 } 081 082 var workRequirementName = SequenceGeneratorLogic.getInstance().getNextSequenceValue(workRequirementSequence); 083 var startTime = session.START_TIME_LONG; 084 var estimatedTimeAllowed = workRequirementScopeDetail.getEstimatedTimeAllowed(); 085 086 if(estimatedTimeAllowed == null) { 087 estimatedTimeAllowed = workRequirementTypeDetail.getEstimatedTimeAllowed(); 088 } 089 090 // If a requiredTime wasn't supplied, and there's an estimatedTimeAllowed available, then take the current 091 // time + the esimtatedTimeAllowed and use that as the requiredTime. 092 if(requiredTime == null && estimatedTimeAllowed != null) { 093 requiredTime = session.START_TIME + estimatedTimeAllowed; 094 } 095 096 var workRequirement = workRequirementControl.createWorkRequirement(workRequirementName, workEffort, workRequirementScope, startTime, 097 requiredTime, createdBy); 098 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(workRequirement.getPrimaryKey()); 099 100 // TODO: should requiredTime map into triggerTime? 101 workflowControl.addEntityToWorkflowUsingNames(null, WorkRequirementStatusConstants.Workflow_WORK_REQUIREMENT_STATUS, 102 assignedParty == null ? WorkRequirementStatusConstants.WorkflowEntrance_NEW_UNASSIGNED : WorkRequirementStatusConstants.WorkflowEntrance_NEW_ASSIGNED, 103 entityInstance, null, null, createdBy); 104 105 if(assignedParty != null) { 106 // If an assignedParty is specified, then create a WorkAssignment and do not give that Party a choice on acceptance. 107 createWorkAssignment(workRequirement, assignedParty, session.START_TIME_LONG, assignedEndTime, 108 WorkAssignmentStatusConstants.WorkflowEntrance_NEW_ACCEPTED, createdBy); 109 } 110 111 return workRequirement; 112 } 113 114 public WorkAssignment createWorkAssignment(final WorkRequirement workRequirement, final Party party, final Long startTime, final Long endTime, 115 final String workflowEntranceName, final BasePK createdBy) { 116 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 117 var workflowControl = Session.getModelController(WorkflowControl.class); 118 var workRequirementControl = Session.getModelController(WorkRequirementControl.class); 119 var workAssignment = workRequirementControl.createWorkAssignment(workRequirement, party, startTime, endTime, createdBy); 120 var workRequirementStatus = workRequirementControl.getWorkRequirementStatusForUpdate(workRequirement); 121 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(workAssignment.getPrimaryKey()); 122 123 workRequirementStatus.setLastWorkAssignment(workAssignment); 124 125 // TODO: endTime should be used as a triggerTime for the Workflow, and dump it into a difference status. Only if the workflowEntranceName 126 // isn't 'NEW_ACCEPTED' perhaps? This could trigger automatic reassignment if it hasn't been forced to a particular party. 127 workflowControl.addEntityToWorkflowUsingNames(null, WorkAssignmentStatusConstants.Workflow_WORK_ASSIGNMENT_STATUS, workflowEntranceName, 128 entityInstance, null, null, createdBy); 129 130 return workAssignment; 131 } 132 133 public WorkTime createWorkTime(final UserVisit userVisit, final WorkRequirement workRequirement, final Party party, final Long startTime, 134 final Long endTime, final boolean complete, final BasePK createdBy) { 135 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 136 var workflowControl = Session.getModelController(WorkflowControl.class); 137 var workRequirementControl = Session.getModelController(WorkRequirementControl.class); 138 var workTime = workRequirementControl.createWorkTime(workRequirement, party, startTime, endTime, createdBy); 139 var workRequirementStatus = workRequirementControl.getWorkRequirementStatusForUpdate(workRequirement); 140 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(workTime.getPrimaryKey()); 141 142 workRequirementStatus.setLastWorkTime(workTime); 143 144 workflowControl.addEntityToWorkflowUsingNames(null, WorkTimeStatusConstants.Workflow_WORK_TIME_STATUS, 145 endTime == null ? WorkTimeStatusConstants.WorkflowEntrance_NEW_IN_PROGRESS : complete ? WorkTimeStatusConstants.WorkflowEntrance_NEW_COMPLETE : WorkTimeStatusConstants.WorkflowEntrance_NEW_INCOMPLETE, 146 entityInstance, null, null, createdBy); 147 148 // If there's a UserVisit and it is "IN_PROGRESS," then set it up so that it'll be ended as "INCOMPLETE" if the 149 // UserVisit is abandoned. 150 if(userVisit != null && endTime != null) { 151 workRequirementControl.createWorkTimeUserVisit(workTime, userVisit); 152 } 153 154 return workTime; 155 } 156 157 public void endWorkTime(final WorkTime workTime, final Long endTime, final boolean complete, final PartyPK endedBy) { 158 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 159 var workflowControl = Session.getModelController(WorkflowControl.class); 160 var workRequirementControl = Session.getModelController(WorkRequirementControl.class); 161 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(workTime.getPrimaryKey()); 162 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceForUpdateUsingNames(WorkTimeStatusConstants.Workflow_WORK_TIME_STATUS, entityInstance); 163 var workflowDestination = WorkflowDestinationLogic.getInstance().getWorkflowDestinationByName(null, workflowEntityStatus.getWorkflowStep(), 164 complete ? WorkTimeStatusConstants.WorkflowDestination_IN_PROGRESS_TO_COMPLETE : WorkTimeStatusConstants.WorkflowDestination_IN_PROGRESS_TO_INCOMPLETE); 165 var workTimeDetailValue = workRequirementControl.getWorkTimeDetailValueForUpdate(workTime); 166 167 workTimeDetailValue.setEndTime(endTime); 168 workRequirementControl.updateWorkTimeFromValue(workTimeDetailValue, endedBy); 169 170 workflowControl.transitionEntityInWorkflow(null, workflowEntityStatus, workflowDestination, null, endedBy); 171 } 172 173 public void endWorkTimesByUserVisit(final UserVisit userVisit, final Long endTime, final PartyPK updatedBy) { 174 var workRequirementControl = Session.getModelController(WorkRequirementControl.class); 175 var workTimeUserVisits = workRequirementControl.getWorkTimeUserVisitsByUserVisitForUpdate(userVisit); 176 177 workTimeUserVisits.stream().map((workTimeUserVisit) -> { 178 endWorkTime(workTimeUserVisit.getWorkTime(), endTime == null ? null : userVisit.getLastCommandTime(), false, updatedBy); 179 return workTimeUserVisit; 180 }).forEach((workTimeUserVisit) -> { 181 workRequirementControl.deleteWorkTimeUserVisit(workTimeUserVisit); 182 }); 183 } 184 185}