View Javadoc

1   package org.kuali.student.lum.program.server;
2   
3   import java.util.List;
4   
5   import org.kuali.student.lum.common.server.StatementUtil;
6   import org.kuali.student.r2.core.versionmanagement.dto.VersionDisplayInfo;
7   import org.kuali.student.r1.core.statement.dto.StatementTreeViewInfo;
8   import org.kuali.student.r2.common.dto.DtoConstants;
9   import org.kuali.student.r2.common.exceptions.DoesNotExistException;
10  import org.kuali.student.r2.common.exceptions.InvalidParameterException;
11  import org.kuali.student.r2.common.util.ContextUtils;
12  import org.kuali.student.r2.common.util.constants.ProgramServiceConstants;
13  import org.kuali.student.r2.lum.program.dto.CoreProgramInfo;
14  import org.kuali.student.r2.lum.program.dto.ProgramRequirementInfo;
15  import org.kuali.student.r2.lum.program.service.ProgramService;
16  import org.springframework.transaction.annotation.Transactional;
17  
18  /**
19   * This class is called whenever the state of a major discipline changes.
20   * <p>
21   * We have a separate class because the operations need to be marked with the @Transactional annotation.
22   * <p>
23   * THIS CLASS IS DUPLICATED FOR MAJOR DISCIPLINE, CORE, AND CREDENTIAL PROGRAMS SINCE THERE ARE DIFFERENT SERVICE METHODS FOR
24   * EACH OF THESE TYPES (THOUGH THEY ARE SIMILAR)
25   * <P>
26   * 
27   * @author Kuali Rice Team (kuali-rice@googlegroups.com)
28   */
29  @Transactional(noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
30  public class CoreProgramStateChangeServiceImpl  implements StateChangeService {
31  
32      
33      private ProgramService programService;
34  
35      /**
36       * This method is called by workflow when the state changes.
37       * 
38       * @param majorDisciplineId
39       * @param state
40       * @return
41       * @throws Exception
42       */
43      public void changeState(String coreProgramId, String newState) throws Exception {
44          // This method will be called from workflow.
45          // Since we cannot activate a program from the workflow we do not need to add endEntryTerm and endEnrollTerm
46          changeState(null, null, null, coreProgramId, newState);
47      }
48  
49      /**
50       * This method is called from the UI (servlet) when state changes.
51       * 
52       * @param endEntryTerm
53       * @param endEnrollTerm
54       * @param programType
55       * @param majorDisciplineId
56       * @param newState
57       * @return
58       * @throws Exception
59       */
60      public void changeState(String endEntryTerm, String endEnrollTerm, String endInstAdmitTerm, String coreProgramId, String newState) throws Exception {
61  
62          // New state must not be null
63          if (newState == null)
64              throw new InvalidParameterException("new state cannot be null");
65  
66          // The version selected in the UI
67          CoreProgramInfo selectedVersion = programService.getCoreProgram(coreProgramId,ContextUtils.getContextInfo());
68  
69          // If we are activating this version we need to mark the previous version superseded,
70          // update the previous version end terms, and make the selected version current.
71          if (newState.equals(DtoConstants.STATE_ACTIVE)) {
72  
73              // Update previous versions to superseded and set end terms on previous current version.
74          	updatePreviousVersions(selectedVersion, endEntryTerm, endEnrollTerm, endInstAdmitTerm);
75          	
76              // Update state of all associated objects for current version
77              // NOTE: we must update state BEFORE making the version current
78              updateCoreProgramInfoState(selectedVersion, newState);
79  
80              // Make this the current version
81              makeCurrent(selectedVersion);
82          } else {
83  
84              // Update state of all associated objects for current version
85              updateCoreProgramInfoState(selectedVersion, newState);
86          }
87  
88      }
89  
90      /**
91       * This method finds all previous versions of program and sets all previous ACTIVE,APPROVED,DRAFT versions to SUPERSEDED and
92       * sets new end terms for previous current version.
93   
94       * @param majorDisciplineInfo The version of major discipline program being activated
95       * @param endEntryTerm The new end entry term to set on previous active version
96       * @param endEnrollTerm The new end enroll term to set on previous active version
97       * @throws Exception
98       */
99      private void updatePreviousVersions (CoreProgramInfo selectedVersion, String endEntryTerm, String endEnrollTerm, String endInstAdmitTerm) throws Exception {
100     	// Get the current version of major discipline given the selected version
101     	CoreProgramInfo currentVersion = getCurrentVersion(selectedVersion);
102     	
103     	boolean isSelectedVersionCurrent = selectedVersion.getId().equals(currentVersion.getId());
104     	
105     	//Set the end terms on the current version of major discipline and update it's state to superseded
106     	setEndTerms(currentVersion, endEntryTerm, endEnrollTerm);
107     	updateCoreProgramInfoState(currentVersion, DtoConstants.STATE_SUPERSEDED);
108 
109 		// Loop through all previous active or approved programs and set the state to superseded.
110 		// We should only need to evaluated versions with sequence number
111 		// higher than previous active program
112 
113 
114 	   	List<VersionDisplayInfo> versions = programService.getVersions(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI,
115     			selectedVersion.getVersion().getVersionIndId(), ContextUtils.getContextInfo());
116 		Long startSeq = new Long(1);
117 
118 		if (!isSelectedVersionCurrent) {
119 			startSeq = currentVersion.getVersion().getSequenceNumber() + 1;
120 		}
121 
122 		for (VersionDisplayInfo versionInfo : versions) {
123 			boolean isVersionNewerThanCurrentVersion = versionInfo.getSequenceNumber() >= startSeq;
124 			boolean isVersionSelectedVersion = versionInfo.getSequenceNumber().equals(selectedVersion.getVersion().getSequenceNumber());  
125 			boolean updateState = isVersionNewerThanCurrentVersion && !isVersionSelectedVersion;
126 			if (updateState) {
127 				CoreProgramInfo otherProgram = programService.getCoreProgram(versionInfo.getId(),ContextUtils.getContextInfo());
128 				if (otherProgram.getStateKey().equals(DtoConstants.STATE_APPROVED) ||
129 					otherProgram.getStateKey().equals(DtoConstants.STATE_ACTIVE)){
130 			        updateCoreProgramInfoState(otherProgram, DtoConstants.STATE_SUPERSEDED);
131 				}		
132 			}
133 		}    	
134 
135     }
136 
137 	/**
138 	 * Get the current version of program given the selected version of program
139 	 * 
140 	 * @param verIndId
141 	 */
142 	protected CoreProgramInfo getCurrentVersion(CoreProgramInfo coreProgramInfo)
143 			throws Exception {
144 		// Get version independent id of program
145 		String verIndId = coreProgramInfo.getVersion().getVersionIndId();
146 
147 		// Get id of current version of program given the version independent id
148 		VersionDisplayInfo curVerDisplayInfo = programService.getCurrentVersion(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI, verIndId,ContextUtils.getContextInfo());
149 		String curVerId = curVerDisplayInfo.getId();
150 
151 		// Return the current version of the course
152 		CoreProgramInfo currentVersion = programService.getCoreProgram(curVerId,ContextUtils.getContextInfo());
153 
154 		return currentVersion;
155 	}
156     
157     /**
158      * This method updates the end terms for the major discipline passed into it.
159      * <p>
160      * You must still call updateState() to save the object using the web service.
161      * 
162      * @param coreProgramInfo
163      * @param endEntryTerm
164      * @param endEnrollTerm
165      */
166     private void setEndTerms(CoreProgramInfo coreProgramInfo, String endEntryTerm, String endEnrollTerm) {
167         coreProgramInfo.setEndProgramEntryTerm(endEntryTerm);
168         coreProgramInfo.setEndTerm(endEnrollTerm);
169     }
170 
171     /**
172      * This method will update the state of this object and all associated objects.
173      * <p>
174      * It is needed because we need to make separate web service calls to update the state of these objects.
175      * 
176      * @param coreProgramInfo
177      * @param newState
178      */
179     private void updateCoreProgramInfoState(CoreProgramInfo coreProgramInfo, String newState) throws Exception {
180         // Update the statement tree
181         List<String> programRequirementIds = coreProgramInfo.getProgramRequirements();
182         updateRequirementsState(programRequirementIds, newState);
183         
184         // Credential and core programs do not have variations
185  
186 
187         // Update major discipline
188         coreProgramInfo.setStateKey(newState);
189   
190         programService.updateCoreProgram(coreProgramInfo.getId(),coreProgramInfo.getTypeKey(),coreProgramInfo,ContextUtils.getContextInfo());
191     }
192 
193     /**
194      * This method will make this version of the major discipline the current one.
195      * 
196      * @param coreProgramInfo
197      */
198     private void makeCurrent(CoreProgramInfo coreProgramInfo) throws Exception {
199 
200         // Check if this is the current version before trying to make it current
201         // (the web service will error if you try to make a version current that is already current)
202     	VersionDisplayInfo currentVersion = programService.getCurrentVersion(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI, coreProgramInfo.getVersion().getVersionIndId(),ContextUtils.getContextInfo());
203 
204         // If this is not the current version, then make it current
205         if (
206         		!currentVersion.getSequenceNumber().equals(coreProgramInfo.getVersion().getSequenceNumber())) {
207             programService.setCurrentCoreProgramVersion(coreProgramInfo.getId(), null,ContextUtils.getContextInfo());
208         }
209     }
210 
211     /**
212      * This method will update the requirement state.
213      * <p>
214      * Note that it uses StatementUtil to update the statement tree.
215      * 
216      * @param coreProgramInfo
217      * @param newState
218      * @throws Exception
219      */
220     public void updateRequirementsState(List<String> programRequirementIds, String newState) throws Exception {
221 
222         for (String programRequirementId : programRequirementIds) {
223 
224             // Get program requirement from the program service
225             ProgramRequirementInfo programRequirementInfo = null;
226             programRequirementInfo = programService.getProgramRequirement(programRequirementId, ContextUtils.getContextInfo());
227 
228             // Look in the requirement for the statement tree
229             StatementTreeViewInfo statementTree = null;
230  
231              statementTree = programRequirementInfo.getStatement();
232 
233             // And recursively update the entire tree with the new state
234             StatementUtil.updateStatementTreeViewInfoState(newState, statementTree);
235 
236             // Update the state of the requirement object
237             programRequirementInfo.setStateKey(newState);
238 
239             // The write the requirement back to the program service
240             programService.updateProgramRequirement(programRequirementInfo.getId(), programRequirementInfo.getTypeKey(), programRequirementInfo, ContextUtils.getContextInfo());
241 
242         }
243     }
244 
245     /**
246      * This method is used by Spring to inject the program service into this bean.
247      * 
248      * @param programService
249      */
250     public void setProgramService(ProgramService programService) {
251         this.programService = programService;
252     }
253 
254 }