001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 package org.kuali.kfs.module.cg.document;
017
018 import java.util.Collection;
019 import java.util.List;
020 import java.util.Map;
021
022 import org.apache.commons.lang.StringUtils;
023 import org.kuali.kfs.module.cg.businessobject.Proposal;
024 import org.kuali.kfs.module.cg.businessobject.ProposalProjectDirector;
025 import org.kuali.kfs.module.cg.businessobject.ProposalResearchRisk;
026 import org.kuali.kfs.module.cg.businessobject.ProposalSubcontractor;
027 import org.kuali.kfs.module.cg.businessobject.ResearchRiskType;
028 import org.kuali.kfs.module.cg.businessobject.defaultvalue.NextProposalNumberFinder;
029 import org.kuali.kfs.module.cg.document.service.RoutingFormResearchRiskService;
030 import org.kuali.kfs.sys.KFSConstants;
031 import org.kuali.kfs.sys.KFSPropertyConstants;
032 import org.kuali.kfs.sys.context.SpringContext;
033 import org.kuali.kfs.sys.document.FinancialSystemMaintainable;
034 import org.kuali.rice.kim.bo.Person;
035 import org.kuali.rice.kim.service.PersonService;
036 import org.kuali.rice.kns.bo.PersistableBusinessObject;
037 import org.kuali.rice.kns.document.MaintenanceDocument;
038 import org.kuali.rice.kns.util.AssertionUtils;
039 import org.kuali.rice.kns.util.ObjectUtils;
040
041 /**
042 * Methods for the Proposal maintenance document UI.
043 */
044 public class ProposalMaintainableImpl extends FinancialSystemMaintainable {
045 public ProposalMaintainableImpl() {
046 super();
047 }
048
049 /**
050 * Constructs a new ProposalMaintainableImpl from an existing {@link Proposal}.
051 *
052 * @param proposal
053 */
054 public ProposalMaintainableImpl(Proposal proposal) {
055 super(proposal);
056 this.setBoClass(proposal.getClass());
057 }
058
059 /**
060 * Use a new proposal number when creating a copy.
061 */
062 @Override
063 public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) {
064 getProposal().setProposalNumber(NextProposalNumberFinder.getLongValue());
065 getProposal().setProposalClosingDate(null);
066 super.processAfterCopy(document, parameters);
067 }
068
069 /**
070 * This method is called for refreshing the {@link Agency} before display to show the full name in case the agency number was
071 * changed by hand before any submit that causes a redisplay.
072 */
073 @Override
074 public void processAfterRetrieve() {
075 refreshProposal(false);
076 super.processAfterRetrieve();
077 }
078
079 /**
080 * <p>
081 * This method is called for refreshing the {@link Agency} before a save to display the full name in case the agency number was
082 * changed by hand just before the save. Also, if there is only one {@link ProjectDirector}, then this method defaults it to be
083 * primary. This method can change data, unlike the rules. It is run before the rules.<p/> This default primary is limited to
084 * save actions (including route, etc) so that when the user adds multiple {@link ProjectDirectors} the first one added doesn't
085 * default to primary (so the user must choose).
086 */
087 @Override
088 public void prepareForSave() {
089 refreshProposal(false);
090 List<ProposalProjectDirector> directors = getProposal().getProposalProjectDirectors();
091 if (directors.size() == 1) {
092 directors.get(0).setProposalPrimaryProjectDirectorIndicator(true);
093 }
094 List<ProposalSubcontractor> proposalSubcontractors = getProposal().getProposalSubcontractors();
095 if (proposalSubcontractors != null && !proposalSubcontractors.isEmpty()) {
096 int i = 0;
097 for (ProposalSubcontractor proposalSubcontractor : proposalSubcontractors) {
098 i++;
099 if (StringUtils.isBlank(proposalSubcontractor.getProposalSubcontractorNumber())) {
100 proposalSubcontractor.setProposalSubcontractorNumber("" + i);
101 }
102 }
103 }
104 super.prepareForSave();
105 }
106
107 /**
108 * This method is called for refreshing the {@link Agency} and other related BOs after a lookup, to display their full name &
109 * etc without AJAX.
110 *
111 * @param refreshCaller
112 * @param fieldValues
113 * @param document
114 */
115 @Override
116 public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) {
117 refreshProposal(KFSConstants.KUALI_LOOKUPABLE_IMPL.equals(fieldValues.get(KFSConstants.REFRESH_CALLER)));
118 super.refresh(refreshCaller, fieldValues, document);
119 }
120
121 /**
122 * This is a hook for initializing the BO from the maintenance framework. It initializes the {@link ResearchRiskType}s
123 * collection.
124 *
125 * @param generateDefaultValues true for initialization
126 */
127 @Override
128 public void setGenerateDefaultValues(String docTypeName) {
129 // if (generateDefaultValues) {
130 initResearchRiskTypes();
131 // }
132 super.setGenerateDefaultValues(docTypeName);
133 }
134
135 /**
136 *
137 */
138 private void initResearchRiskTypes() {
139 List<ProposalResearchRisk> risks = getProposal().getProposalResearchRisks();
140 AssertionUtils.assertThat(risks.isEmpty());
141 // no requirement to exclude any risk types (except inactive ones, which the service excludes anyway)
142 final String[] riskTypeCodesToExclude = new String[0];
143 List<ResearchRiskType> researchRiskTypes = SpringContext.getBean(RoutingFormResearchRiskService.class).getResearchRiskTypes(riskTypeCodesToExclude);
144 for (ResearchRiskType type : researchRiskTypes) {
145 ProposalResearchRisk ppr = new ProposalResearchRisk();
146 ppr.setResearchRiskTypeCode(type.getResearchRiskTypeCode());
147 ppr.setResearchRiskType(type); // one less refresh
148 risks.add(ppr);
149 }
150 }
151
152 /**
153 * @param refreshFromLookup
154 */
155 private void refreshProposal(boolean refreshFromLookup) {
156 getProposal().refreshNonUpdateableReferences();
157
158 getNewCollectionLine(KFSPropertyConstants.PROPOSAL_SUBCONTRACTORS).refreshNonUpdateableReferences();
159
160 refreshNonUpdateableReferences(getProposal().getProposalOrganizations());
161 refreshNonUpdateableReferences(getProposal().getProposalSubcontractors());
162 refreshNonUpdateableReferences(getProposal().getProposalResearchRisks());
163
164 refreshProposalProjectDirectors(refreshFromLookup);
165 }
166
167 /**
168 * Refreshes this maintainable's ProposalProjectDirectors.
169 *
170 * @param refreshFromLookup a lookup returns only the primary key, so ignore the secondary key when true
171 */
172 private void refreshProposalProjectDirectors(boolean refreshFromLookup) {
173 if (refreshFromLookup) {
174 getNewCollectionLine(KFSPropertyConstants.PROPOSAL_PROJECT_DIRECTORS).refreshNonUpdateableReferences();
175 refreshNonUpdateableReferences(getProposal().getProposalProjectDirectors());
176 }
177 else {
178 refreshWithSecondaryKey((ProposalProjectDirector) getNewCollectionLine(KFSPropertyConstants.PROPOSAL_PROJECT_DIRECTORS));
179 for (ProposalProjectDirector ppd : getProposal().getProposalProjectDirectors()) {
180 refreshWithSecondaryKey(ppd);
181 }
182 }
183 }
184
185 /**
186 * @param collection
187 */
188 private static void refreshNonUpdateableReferences(Collection<? extends PersistableBusinessObject> collection) {
189 for (PersistableBusinessObject item : collection) {
190 item.refreshNonUpdateableReferences();
191 }
192 }
193
194 /**
195 * Refreshes the reference to ProjectDirector, giving priority to its secondary key. Any secondary key that it has may be user
196 * input, so that overrides the primary key, setting the primary key. If its primary key is blank or nonexistent, then leave the
197 * current reference as it is, because it may be a nonexistent instance which is holding the secondary key (the username, i.e.,
198 * principalName) so we can redisplay it to the user for correction. If it only has a primary key then use that, because it may
199 * be coming from the database, without any user input.
200 *
201 * @param ppd the ProposalProjectDirector to refresh
202 */
203 private static void refreshWithSecondaryKey(ProposalProjectDirector ppd) {
204 String secondaryKey = null;
205 if (ObjectUtils.isNotNull(ppd.getProjectDirector())) {
206 secondaryKey = ppd.getProjectDirector().getPrincipalName();
207 }
208 if (StringUtils.isNotBlank(secondaryKey)) {
209 Person dir = SpringContext.getBean(PersonService.class).getPersonByPrincipalName(secondaryKey);
210 ppd.setPrincipalId(dir == null ? null : dir.getPrincipalId());
211 }
212 if (StringUtils.isNotBlank(ppd.getPrincipalId())) {
213 Person person = SpringContext.getBean(PersonService.class).getPerson(ppd.getPrincipalId());
214 if (person != null) {
215 ppd.refreshNonUpdateableReferences();
216 }
217 }
218 }
219
220 /**
221 * Gets the {@link Proposal}
222 *
223 * @return
224 */
225 public Proposal getProposal() {
226 return (Proposal) getBusinessObject();
227 }
228
229 /**
230 * called for refreshing the {@link Subcontractor} on {@link ProposalSubcontractor} before adding to the
231 * {@link ProposalSubcontractor}s collection on the proposal. this is to ensure that the summary fields are shown correctly.
232 * i.e. {@link Subcontractor} name
233 *
234 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#addNewLineToCollection(java.lang.String)
235 */
236 @Override
237 public void addNewLineToCollection(String collectionName) {
238 refreshProposal(false);
239 super.addNewLineToCollection(collectionName);
240 }
241 }