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.sys.batch.dataaccess.impl;
017    
018    import java.util.ArrayList;
019    import java.util.HashSet;
020    import java.util.List;
021    import java.util.Set;
022    
023    import org.apache.commons.beanutils.PropertyUtils;
024    import org.apache.log4j.Logger;
025    import org.apache.ojb.broker.query.Criteria;
026    import org.kuali.kfs.sys.KFSConstants;
027    import org.kuali.kfs.sys.KFSPropertyConstants;
028    import org.kuali.kfs.sys.batch.dataaccess.FiscalYearMaker;
029    import org.kuali.kfs.sys.businessobject.SystemOptions;
030    import org.kuali.rice.kns.bo.Inactivateable;
031    import org.kuali.rice.kns.bo.PersistableBusinessObject;
032    import org.kuali.rice.kns.dao.impl.PlatformAwareDaoBaseOjb;
033    import org.kuali.rice.kns.service.PersistenceStructureService;
034    import org.kuali.rice.kns.util.Guid;
035    
036    /**
037     * Default implementation of fiscal year maker process for an entity. This implementation can be used for a table in the fiscal year
038     * maker process by defining a spring bean and setting the businessObjectClass property.
039     */
040    public class FiscalYearMakerImpl extends PlatformAwareDaoBaseOjb implements FiscalYearMaker {
041        private static Logger LOG = org.apache.log4j.Logger.getLogger(FiscalYearMakerImpl.class);
042    
043        private PersistenceStructureService persistenceStructureService;
044    
045        private Class<? extends PersistableBusinessObject> businessObjectClass;
046        private Set<Class<? extends PersistableBusinessObject>> parentClasses;
047    
048        private boolean fiscalYearOneBehind;
049        private boolean fiscalYearOneAhead;
050        private boolean twoYearCopy;
051        private boolean carryForwardInactive;
052        private boolean allowOverrideTargetYear;
053    
054        /**
055         * Constructs a FiscalYearMakerImpl.java.
056         */
057        public FiscalYearMakerImpl() {
058            fiscalYearOneBehind = false;
059            fiscalYearOneAhead = false;
060            twoYearCopy = false;
061            carryForwardInactive = false;
062            allowOverrideTargetYear = true;
063            parentClasses = new HashSet<Class<? extends PersistableBusinessObject>>();
064        }
065    
066        /**
067         * Sets fiscal year field up one, resets version number and assigns a new Guid for the object id
068         * 
069         * @see org.kuali.kfs.coa.dataaccess.FiscalYearMaker#changeForNewYear(java.lang.Integer,
070         *      org.kuali.rice.kns.bo.PersistableBusinessObject)
071         */
072        public void changeForNewYear(Integer baseFiscalYear, PersistableBusinessObject currentRecord) {
073            LOG.debug("starting changeForNewYear() for bo class " + businessObjectClass.getName());
074    
075            try {
076                // increment fiscal year by 1
077                Integer fiscalYear = (Integer) PropertyUtils.getProperty(currentRecord, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR);
078                Integer newFiscalYear = fiscalYear + 1;
079    
080                // update extension, must be done before updating main record so we can retrieve the extension record by reference
081                updateExtensionRecord(newFiscalYear, currentRecord);
082    
083                // update main record fields
084                PropertyUtils.setSimpleProperty(currentRecord, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, newFiscalYear);
085    
086                currentRecord.setVersionNumber(new Long(1));
087                currentRecord.setObjectId(new Guid().toString());
088            }
089            catch (Exception e) {
090                String msg = String.format("Failed to set properties for class %s due to %s", businessObjectClass.getName(), e.getMessage());
091                LOG.error(msg);
092                throw new RuntimeException(msg, e);
093            }
094        }
095    
096        /**
097         * Determines if an extension record is mapped up and exists for the current record. If so then updates the version number,
098         * object id, and clears the primary keys so they will be relinked when storing the main record
099         * 
100         * @param newFiscalYear fiscal year to set
101         * @param currentRecord main record with possible extension reference
102         */
103        protected void updateExtensionRecord(Integer newFiscalYear, PersistableBusinessObject currentRecord) {
104            // check if reference is mapped up
105            if (!persistenceStructureService.hasReference(businessObjectClass, KFSPropertyConstants.EXTENSION)) {
106                return;
107            }
108    
109            // try to retrieve extension record
110            currentRecord.refreshReferenceObject(KFSPropertyConstants.EXTENSION);
111            PersistableBusinessObject extension = currentRecord.getExtension();
112    
113            // if found then update fields
114            if (extension != null) {
115                extension.setVersionNumber(new Long(1));
116                extension.setObjectId(new Guid().toString());
117    
118                // clear pk fields so they will be relinked
119                persistenceStructureService.clearPrimaryKeyFields(extension);
120            }
121        }
122    
123        /**
124         * Selects records for the given base year or base year minus one if this is a lagging copy. If this is a two year copy base
125         * year plus one records will be selected as well. In addition will only select active records if the business object class
126         * implements the Inactivateable interface and has the active property.
127         * 
128         * @see org.kuali.rice.kns.bo.Inactivateable
129         * @see org.kuali.kfs.coa.dataaccess.FiscalYearMaker#createSelectionCriteria(java.lang.Integer)
130         */
131        public Criteria createSelectionCriteria(Integer baseFiscalYear) {
132            LOG.debug("starting createSelectionCriteria() for bo class " + businessObjectClass.getName());
133    
134            Criteria criteria = new Criteria();
135            addYearCriteria(criteria, baseFiscalYear, false);
136    
137            // add active criteria if the business object class supports the inactivateable interface
138            List<String> fields = persistenceStructureService.listFieldNames(businessObjectClass);
139            if (Inactivateable.class.isAssignableFrom(businessObjectClass) && fields.contains(KFSPropertyConstants.ACTIVE) && !carryForwardInactive) {
140                criteria.addEqualTo(KFSPropertyConstants.ACTIVE, KFSConstants.ACTIVE_INDICATOR);
141            }
142    
143            return criteria;
144        }
145    
146        /**
147         * Selects records to delete for base year + 1 (or base year for lagging, and base year + 2 for two year)
148         * 
149         * @see org.kuali.kfs.coa.batch.dataaccess.FiscalYearMakerHelper#createDeleteCriteria(java.lang.Integer)
150         */
151        public Criteria createDeleteCriteria(Integer baseFiscalYear) {
152            LOG.debug("starting createDeleteCriteria() for bo class " + businessObjectClass.getName());
153    
154            Criteria criteria = new Criteria();
155            addYearCriteria(criteria, baseFiscalYear + 1, twoYearCopy);
156    
157            return criteria;
158        }
159    
160        /**
161         * Adds fiscal year criteria based on the configuration (copy two years, lagging, or normal)
162         * 
163         * @param criteria OJB Criteria object
164         * @param baseFiscalYear Fiscal year for critiera
165         * @param createTwoYears indicates whether two years of fiscal year criteria should be added
166         */
167        protected void addYearCriteria(Criteria criteria, Integer baseFiscalYear, boolean createTwoYears) {
168            verifyUniversityFiscalYearPropertyExists();
169    
170            if (fiscalYearOneBehind) {
171                baseFiscalYear = baseFiscalYear - 1;
172            }
173            else if (fiscalYearOneAhead) {
174                baseFiscalYear = baseFiscalYear + 1;
175            }
176    
177            if (createTwoYears) {
178                List<Integer> copyYears = new ArrayList<Integer>();
179                copyYears.add(baseFiscalYear);
180                copyYears.add(baseFiscalYear + 1);
181    
182                criteria.addIn(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, copyYears);
183            }
184            else {
185                criteria.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, baseFiscalYear);
186            }
187        }
188    
189        /**
190         * Verifies the given business object class has the university fiscal year property necessary for setting default criteria
191         * 
192         * @see org.kuali.kfs.sys.KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR
193         */
194        protected void verifyUniversityFiscalYearPropertyExists() {
195            List<String> fields = persistenceStructureService.listFieldNames(businessObjectClass);
196            if (fields == null || !fields.contains(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR)) {
197                String msg = String.format("No %s property in business object class %s", KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, businessObjectClass.getName());
198                LOG.error(msg);
199                throw new RuntimeException(msg);
200            }
201        }
202    
203        /**
204         * Default implementation does nothing
205         * 
206         * @see org.kuali.kfs.coa.batch.dataaccess.FiscalYearMakerHelper#performCustomProcessing(java.lang.Integer)
207         */
208        public void performCustomProcessing(Integer baseFiscalYear, boolean firstCopyYear) {
209    
210        }
211    
212        /**
213         * Default to doing both normal FYM process and custom
214         * 
215         * @see org.kuali.kfs.coa.batch.dataaccess.FiscalYearMakerHelper#doCustomProcessingOnly()
216         */
217        public boolean doCustomProcessingOnly() {
218            return false;
219        }
220    
221        /**
222         * @see org.kuali.kfs.coa.dataaccess.FiscalYearMaker#getBusinessObjectClass()
223         */
224        public Class<? extends PersistableBusinessObject> getBusinessObjectClass() {
225            return businessObjectClass;
226        }
227    
228        /**
229         * <code>Options</code> is the parent for univFiscalYear which all our copy objects should have. Added to list here by default.
230         * 
231         * @see org.kuali.kfs.coa.batch.dataaccess.FiscalYearMakerHelper#getParentClasses()
232         * @see org.kuali.kfs.sys.businessobject.Options
233         */
234        public Set<Class<? extends PersistableBusinessObject>> getParentClasses() {
235            if (!businessObjectClass.equals(SystemOptions.class) && !parentClasses.contains(SystemOptions.class)) {
236                parentClasses.add(SystemOptions.class);
237            }
238    
239            return parentClasses;
240        }
241    
242        /**
243         * Sets the businessObjectClass attribute value.
244         * 
245         * @param businessObjectClass The businessObjectClass to set.
246         */
247        public void setBusinessObjectClass(Class<? extends PersistableBusinessObject> businessObjectClass) {
248            this.businessObjectClass = businessObjectClass;
249        }
250    
251        /**
252         * Sets the parentClasses attribute value.
253         * 
254         * @param parentClasses The parentClasses to set.
255         */
256        public void setParentClasses(Set<Class<? extends PersistableBusinessObject>> parentClasses) {
257            this.parentClasses = parentClasses;
258        }
259    
260        /**
261         * Gets the fiscalYearOneBehind attribute.
262         * 
263         * @return Returns the fiscalYearOneBehind.
264         */
265        public boolean isFiscalYearOneBehind() {
266            return fiscalYearOneBehind;
267        }
268    
269        /**
270         * Sets the fiscalYearOneBehind attribute value.
271         * 
272         * @param fiscalYearOneBehind The fiscalYearOneBehind to set.
273         */
274        public void setFiscalYearOneBehind(boolean fiscalYearOneBehind) {
275            this.fiscalYearOneBehind = fiscalYearOneBehind;
276        }
277    
278        /**
279         * Gets the fiscalYearOneAhead attribute.
280         * 
281         * @return Returns the fiscalYearOneAhead.
282         */
283        public boolean isFiscalYearOneAhead() {
284            return fiscalYearOneAhead;
285        }
286    
287        /**
288         * Sets the fiscalYearOneAhead attribute value.
289         * 
290         * @param fiscalYearOneAhead The fiscalYearOneAhead to set.
291         */
292        public void setFiscalYearOneAhead(boolean fiscalYearOneAhead) {
293            this.fiscalYearOneAhead = fiscalYearOneAhead;
294        }
295    
296        /**
297         * Gets the twoYearCopy attribute.
298         * 
299         * @return Returns the twoYearCopy.
300         */
301        public boolean isTwoYearCopy() {
302            return twoYearCopy;
303        }
304    
305        /**
306         * Sets the twoYearCopy attribute value.
307         * 
308         * @param twoYearCopy The twoYearCopy to set.
309         */
310        public void setTwoYearCopy(boolean twoYearCopy) {
311            this.twoYearCopy = twoYearCopy;
312        }
313    
314        /**
315         * Gets the carryForwardInactive attribute.
316         * 
317         * @return Returns the carryForwardInactive.
318         */
319        public boolean isCarryForwardInactive() {
320            return carryForwardInactive;
321        }
322    
323        /**
324         * Sets the carryForwardInactive attribute value.
325         * 
326         * @param carryForwardInactive The carryForwardInactive to set.
327         */
328        public void setCarryForwardInactive(boolean carryForwardInactive) {
329            this.carryForwardInactive = carryForwardInactive;
330        }
331    
332        /**
333         * Sets the persistenceStructureService attribute value.
334         * 
335         * @param persistenceStructureService The persistenceStructureService to set.
336         */
337        public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
338            this.persistenceStructureService = persistenceStructureService;
339        }
340    
341        /**
342         * Gets the allowOverrideTargetYear attribute.
343         * 
344         * @return Returns the allowOverrideTargetYear.
345         */
346        public boolean isAllowOverrideTargetYear() {
347            return allowOverrideTargetYear;
348        }
349    
350        /**
351         * Sets the allowOverrideTargetYear attribute value.
352         * 
353         * @param allowOverrideTargetYear The allowOverrideTargetYear to set.
354         */
355        public void setAllowOverrideTargetYear(boolean allowOverrideTargetYear) {
356            this.allowOverrideTargetYear = allowOverrideTargetYear;
357        }
358    
359    }