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    }