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.lum.program.client.ProgramConstants;
7   import org.kuali.student.r1.core.statement.dto.StatementTreeViewInfo;
8   import org.kuali.student.r2.common.dto.AttributeInfo;
9   import org.kuali.student.r2.common.dto.DtoConstants;
10  import org.kuali.student.r2.common.exceptions.DoesNotExistException;
11  import org.kuali.student.r2.common.exceptions.InvalidParameterException;
12  import org.kuali.student.r2.common.util.ContextUtils;
13  import org.kuali.student.r2.common.util.constants.ProgramServiceConstants;
14  import org.kuali.student.r2.core.versionmanagement.dto.VersionDisplayInfo;
15  import org.kuali.student.r2.lum.program.dto.MajorDisciplineInfo;
16  import org.kuali.student.r2.lum.program.dto.ProgramRequirementInfo;
17  import org.kuali.student.r2.lum.program.service.ProgramService;
18  import org.springframework.transaction.annotation.Transactional;
19  
20  /**
21   * This class is called whenever the state of a major discipline changes.
22   * <p>
23   * We have a separate class because the operations need to be marked with the @Transactional annotation.
24   * <p>
25   * THIS CLASS IS DUPLICATED FOR MAJOR DISCIPLINE, CORE, AND CREDENTIAL PROGRAMS SINCE THERE ARE DIFFERENT
26   * SERVICE METHODS FOR EACH OF THESE TYPES (THOUGH THEY ARE SIMILAR)
27   * <P>
28   * @author Kuali Rice Team (kuali-rice@googlegroups.com)
29   */
30  @Transactional(noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
31  public class ProgramStateChangeServiceImpl {
32  
33      /**
34       * The program service - injected by spring.
35       */
36      private ProgramService programService;
37  
38   
39  
40      /**
41       * This method is called by workflow when the state changes.
42       * 
43       * @param majorDisciplineId
44       * @param state
45       * @return
46       * @throws Exception
47       */
48      public void changeState( String majorDisciplineId, String newState) throws Exception {
49          // This method will be called from workflow.
50          // Since we cannot activate a program from the workflow we do not need to add endEntryTerm and endEnrollTerm
51          changeState(null, null, null, majorDisciplineId, newState);
52      }
53  
54      /**
55       * This method is called from the UI (servlet) when state changes.
56       * 
57       * @param endEntryTerm
58       * @param endEnrollTerm
59       * @param programType
60       * @param majorDisciplineId
61       * @param newState
62       * @return
63       * @throws Exception
64       */
65      public void changeState(String endEntryTerm, String endEnrollTerm, String endInstAdmitTerm,  String majorDisciplineId, String newState) throws Exception {
66  
67          // New state must not be null
68          if (newState == null)
69              throw new InvalidParameterException("new state cannot be null");
70  
71          // The version selected in the UI
72          MajorDisciplineInfo selectedVersion = programService.getMajorDiscipline(majorDisciplineId,ContextUtils.getContextInfo());
73  
74          // If we are activating this version we need to mark the previous version superseded,
75          // update the previous version end terms, and make the selected version current.
76          if (newState.equals(DtoConstants.STATE_ACTIVE)) {
77  
78              // Find the previous version
79              MajorDisciplineInfo previousVersion = findPreviousVersion(selectedVersion);
80  
81              if (previousVersion != null) {
82  
83                  // Set end terms on previous version
84                  setEndTerms(previousVersion, endEntryTerm, endEnrollTerm, endInstAdmitTerm);
85  
86                  // Mark previous version as superseded and update state on all associated objects
87                  updateMajorDisciplineInfoState(previousVersion, DtoConstants.STATE_SUPERSEDED);
88              }
89  
90              // Update state of all associated objects for current version
91              // NOTE: we must update state BEFORE making the version current
92              updateMajorDisciplineInfoState(selectedVersion, newState);
93  
94              // Make this the current version
95              makeCurrent(selectedVersion);
96          } else {
97  
98              // Update state of all associated objects for current version
99              updateMajorDisciplineInfoState(selectedVersion, newState);
100         }
101 
102         
103     }
104 
105     /**
106      * This method updates the end terms for the major discipline passed into it.
107      * <p>
108      * You must still call updateState() to save the object using the web service.
109      * 
110      * @param majorDisciplineInfo
111      * @param endEntryTerm
112      * @param endEnrollTerm
113      * @param endInstAdmitTerm 
114      */
115     private void setEndTerms(MajorDisciplineInfo majorDisciplineInfo, String endEntryTerm, String endEnrollTerm, String endInstAdmitTerm) {
116         majorDisciplineInfo.setEndProgramEntryTerm(endEntryTerm);
117         majorDisciplineInfo.setEndTerm(endEnrollTerm);
118         majorDisciplineInfo.getAttributes().add(new AttributeInfo(ProgramConstants.END_INSTITUTIONAL_ADMIT_TERM, endInstAdmitTerm));
119     }
120 
121     /**
122      * This method will update the state of this object and all associated objects.
123      * <p>
124      * It is needed because we need to make separate web service calls to update the state of these objects.
125      * 
126      * @param majorDisciplineInfo
127      * @param newState
128      */
129     private void updateMajorDisciplineInfoState(MajorDisciplineInfo majorDisciplineInfo, String newState) throws Exception {
130         // Update the statement tree
131         updateRequirementsState(majorDisciplineInfo, newState);
132          
133         // Update major discipline
134         majorDisciplineInfo.setStateKey(newState);
135         programService.updateMajorDiscipline(majorDisciplineInfo.getId(), majorDisciplineInfo, ContextUtils.getContextInfo());
136     }
137 
138     /**
139      * This method will make this version of the major discipline the current one.
140      * 
141      * @param majorDisciplineInfo
142      */
143     private void makeCurrent(MajorDisciplineInfo majorDisciplineInfo) throws Exception {
144 
145         // Check if this is the current version before trying to make it current
146         // (the web service will error if you try to make a version current that is already current)
147         VersionDisplayInfo currentVersion = programService.getCurrentVersion(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI, majorDisciplineInfo.getVersion().getVersionIndId(),ContextUtils.getContextInfo());
148 
149         // If this is not the current version, then make it current
150         if (!currentVersion.getSequenceNumber().equals(majorDisciplineInfo.getVersion().getSequenceNumber())) { programService.setCurrentMajorDisciplineVersion(majorDisciplineInfo.getId(), null,ContextUtils.getContextInfo()); }
151     }
152 
153     /**
154      * This method finds the previous version (the version right before this one).
155      * <p>
156      * e.g. v1 = ACTIVE v2 = APPROVED
157      * <p>
158      * If you passed v2 into this method, it would return v1.
159      * <p>
160      * If there is only one major discipline this method will return null.
161      * 
162      * @param majorDisciplineInfo
163      * @return
164      */
165     private MajorDisciplineInfo findPreviousVersion(MajorDisciplineInfo majorDisciplineInfo) throws Exception {
166         // Find all previous versions using the version independent indicator
167         List<VersionDisplayInfo> versions = programService.getVersions(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI, majorDisciplineInfo.getVersion().getVersionIndId(),ContextUtils.getContextInfo());
168 
169         // Take the sequence number for this version
170         Long sequenceNumber = null;
171  
172         sequenceNumber = majorDisciplineInfo.getVersion().getSequenceNumber();
173 
174         // And subtract 1 from the sequence number to get the previous version
175         sequenceNumber -= 1;
176 
177         // Loop over all versions and find the previous version based on the sequence number
178         /*
179          * NOTE: Dan suggested we loop over all versions and change any version with state=active to
180          *       state=superseded.  However, we decided not to go that route because we would need
181          *       to pull back all data for each version to determine if a version is active, since
182          *       versioninfo does not have a getState() method
183          */
184         MajorDisciplineInfo previousVersion = null;
185         for (VersionDisplayInfo versionInfo : versions) {
186             if (versionInfo.getSequenceNumber().equals(sequenceNumber)) {
187                 previousVersion = programService.getMajorDiscipline(versionInfo.getId(),ContextUtils.getContextInfo());
188                 break;
189             }
190         }
191         return previousVersion;
192     }
193 
194     /**
195      * This method will update the requirement state.
196      * <p>
197      * Note that it uses StatementUtil to update the statement tree.
198      * 
199      * @param majorDisciplineInfo
200      * @param newState
201      * @throws Exception
202      */
203     public void updateRequirementsState(MajorDisciplineInfo majorDisciplineInfo, String newState) throws Exception {
204 
205         // Loop over list of requirement ids returned in the major discipline object
206         List<String> programRequirementIds = majorDisciplineInfo.getProgramRequirements();
207         for (String programRequirementId : programRequirementIds) {
208 
209             // Get program requirement from the program service
210             ProgramRequirementInfo programRequirementInfo = null;
211             programRequirementInfo = programService.getProgramRequirement(programRequirementId, ContextUtils.getContextInfo());
212 
213             // Look in the requirement for the statement tree
214             StatementTreeViewInfo statementTree = programRequirementInfo.getStatement();
215 
216             // And recursively update the entire tree with the new state
217             StatementUtil.updateStatementTreeViewInfoState(newState, statementTree);
218 
219             // Update the state of the requirement object
220             programRequirementInfo.setStateKey(newState);
221 
222             // The write the requirement back to the program service
223 
224             programService.updateProgramRequirement(programRequirementInfo.getId(), programRequirementInfo.getTypeKey(), programRequirementInfo, ContextUtils.getContextInfo());
225 
226         }
227     }
228  
229     /**
230      * This method is used by Spring to inject the program service into this bean.
231      * 
232      * @param programService
233      */
234     public void setProgramService(ProgramService programService) {
235         this.programService = programService;
236     }
237 
238 }