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.coa.document;
017    
018    import java.security.GeneralSecurityException;
019    import java.sql.Date;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.kfs.coa.businessobject.AccountDelegate;
025    import org.kuali.kfs.coa.businessobject.AccountDelegateGlobal;
026    import org.kuali.kfs.coa.service.AccountDelegateService;
027    import org.kuali.kfs.sys.KFSConstants;
028    import org.kuali.kfs.sys.context.SpringContext;
029    import org.kuali.kfs.sys.document.FinancialSystemMaintainable;
030    import org.kuali.rice.core.service.EncryptionService;
031    import org.kuali.rice.kim.service.RoleManagementService;
032    import org.kuali.rice.kns.bo.DocumentHeader;
033    import org.kuali.rice.kns.document.MaintenanceDocument;
034    import org.kuali.rice.kns.document.MaintenanceLock;
035    import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
036    import org.kuali.rice.kns.service.DataDictionaryService;
037    import org.kuali.rice.kns.service.DateTimeService;
038    import org.kuali.rice.kns.service.KNSServiceLocator;
039    import org.kuali.rice.kns.util.ObjectUtils;
040    
041    /**
042     * This class is a special implementation of Maintainable specifically for Account Delegates. It was created to correctly update the
043     * default Start Date on edits and copies, ala JIRA #KULRNE-62.
044     */
045    public class AccountDelegateMaintainableImpl extends FinancialSystemMaintainable {
046        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountDelegateMaintainableImpl.class);
047    
048        /**
049         * This method will reset AccountDelegate's Start Date to the current timestamp on edits and copies
050         * 
051         * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterRetrieve()
052         */
053        @Override
054        public void processAfterCopy( MaintenanceDocument document, Map<String,String[]> parameters ) {
055            this.setStartDateDefault();
056            super.processAfterCopy( document, parameters );
057        }
058    
059        /**
060         * This method will reset AccountDelegate's Start Date to the current timestamp on edits and copies
061         * 
062         * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterEdit()
063         */
064        @Override
065        public void processAfterEdit( MaintenanceDocument document, Map<String,String[]> parameters ) {
066            this.setStartDateDefault();
067            super.processAfterEdit( document, parameters );
068        }
069    
070        /**
071         * This method sets the start date on {@link Delegate} BO
072         */
073        protected void setStartDateDefault() {
074            if (this.businessObject != null && this.businessObject instanceof AccountDelegate) {
075                AccountDelegate delegate = (AccountDelegate) this.businessObject;
076                delegate.setAccountDelegateStartDate(new Date(SpringContext.getBean(DateTimeService.class).getCurrentDate().getTime()));
077            }
078        }
079    
080        /**
081         * Generates the appropriate maintenance locks for the {@link Delegate}
082         * 
083         * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#generateMaintenanceLocks()
084         */
085        @Override
086        public List<MaintenanceLock> generateMaintenanceLocks() {
087            AccountDelegate delegate = (AccountDelegate) this.businessObject;
088            List<MaintenanceLock> locks = super.generateMaintenanceLocks();
089            if (delegate.isAccountsDelegatePrmrtIndicator()) {
090                locks.add(createMaintenanceLock(new String[] { "chartOfAccountsCode", "accountNumber", "financialDocumentTypeCode", "accountsDelegatePrmrtIndicator" }));
091            }
092            return locks;
093        }
094    
095        @Override
096        public String getLockingDocumentId() {
097           String lock = super.getLockingDocumentId();
098           if (StringUtils.isNotBlank(lock))
099               return lock;
100           else {
101               AccountDelegateService accountDelegateService = SpringContext.getBean(AccountDelegateService.class);
102               lock = accountDelegateService.getLockingDocumentId(this, this.documentNumber);
103               return lock;
104           }
105        }
106        
107        /**
108         * This method creates a maintenance lock for the field names supplied
109         * 
110         * @param fieldNames
111         * @return the maintenance lock for supplied field names
112         */
113        protected MaintenanceLock createMaintenanceLock(String[] fieldNames) {
114            MaintenanceLock lock = new MaintenanceLock();
115            lock.setDocumentNumber(this.documentNumber);
116            lock.setLockingRepresentation(createLockingRepresentation(fieldNames));
117            return lock;
118    
119        }
120    
121        /**
122         * This method create a locking representation for the field names supplied
123         * 
124         * @param fieldNames
125         * @return locking representation string
126         */
127        protected String createLockingRepresentation(String[] fieldNames) {
128            StringBuilder lockRepresentation = new StringBuilder();
129    
130            lockRepresentation.append(AccountDelegate.class.getName());
131            lockRepresentation.append(KFSConstants.Maintenance.AFTER_CLASS_DELIM);
132    
133            DataDictionaryService dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
134            EncryptionService encryptionService = KNSServiceLocator.getEncryptionService();
135    
136            int count = 0;
137            for (String fieldName : fieldNames) {
138                lockRepresentation.append(fieldName);
139                lockRepresentation.append(KFSConstants.Maintenance.AFTER_FIELDNAME_DELIM);
140                lockRepresentation.append(retrieveFieldValueForLock(fieldName, dataDictionaryService, encryptionService));
141                if (count < (fieldNames.length - 1)) {
142                    lockRepresentation.append(KFSConstants.Maintenance.AFTER_VALUE_DELIM);
143                }
144                count += 1;
145            }
146    
147    
148            return lockRepresentation.toString();
149        }
150    
151        /**
152         * This method returns the field value of a given field, converting the value to a String and encrypting it if necessary
153         * 
154         * @param fieldName
155         * @param ddService
156         * @return string field value for a lock
157         */
158        protected String retrieveFieldValueForLock(String fieldName, DataDictionaryService ddService, EncryptionService encryptionService) {
159            Object fieldValue = ObjectUtils.getPropertyValue(this.businessObject, fieldName);
160            if (fieldValue == null) {
161                fieldValue = "";
162            }
163    
164            // check if field is a secure
165            if (SpringContext.getBean(BusinessObjectAuthorizationService.class).attributeValueNeedsToBeEncryptedOnFormsAndLinks(getBoClass(), fieldName)) {
166                try {
167                    fieldValue = encryptionService.encrypt(fieldValue);
168                }
169                catch (GeneralSecurityException e) {
170                    LOG.error("Unable to encrypt secure field for locking representation " + e.getMessage());
171                    throw new RuntimeException("Unable to encrypt secure field for locking representation " + e.getMessage());
172                }
173            }
174            return String.valueOf(fieldValue);
175        }
176        
177        /**
178         * This method created a MaintenanceLock for the chartOfAccountsCode and accountNumber for an AccountDelegateGlobal.
179         *
180         * @return the MainenanceLock
181         */
182        
183        public MaintenanceLock createGlobalAccountLock() {
184            
185            String[] fields = {"chartOfAccountsCode", "accountNumber"};
186            MaintenanceLock lock = new MaintenanceLock();
187            lock.setDocumentNumber(this.documentNumber);
188            
189            StringBuilder lockRepresentation = new StringBuilder();
190    
191            lockRepresentation.append(AccountDelegateGlobal.class.getName());
192            lockRepresentation.append(KFSConstants.Maintenance.AFTER_CLASS_DELIM);
193    
194            DataDictionaryService dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
195            EncryptionService encryptionService = KNSServiceLocator.getEncryptionService();
196    
197            int count = 0;
198            for (String fieldName : fields) {
199                lockRepresentation.append(fieldName);
200                lockRepresentation.append(KFSConstants.Maintenance.AFTER_FIELDNAME_DELIM);
201                lockRepresentation.append(retrieveFieldValueForLock(fieldName, dataDictionaryService, encryptionService));
202                if (count < (fields.length - 1)) {
203                    lockRepresentation.append(KFSConstants.Maintenance.AFTER_VALUE_DELIM);
204                }
205                count += 1;
206            }
207    
208            lock.setLockingRepresentation(lockRepresentation.toString());
209            
210            return lock;
211        }
212    
213        /**
214         * Overridden so that after account delegate is saved, it updates the proper account delegate role
215         * Defers saving to a service to guarantee that the delegate saves in a separate transaction
216         * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#saveBusinessObject()
217         */
218        @Override
219        public void saveBusinessObject() {
220            final AccountDelegate accountDelegate = (AccountDelegate)getBusinessObject();
221            final AccountDelegateService accountDelegateService = SpringContext.getBean(AccountDelegateService.class);
222            
223            accountDelegateService.saveForMaintenanceDocument(accountDelegate);
224            
225            accountDelegateService.updateDelegationRole();
226        }
227    
228        
229    }