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.cam.service.impl; 017 018 import java.util.ArrayList; 019 import java.util.Collection; 020 import java.util.HashMap; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.Properties; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.apache.log4j.Logger; 027 import org.kuali.kfs.module.cab.CabConstants; 028 import org.kuali.kfs.module.cab.CabPropertyConstants; 029 import org.kuali.kfs.module.cam.CamsConstants; 030 import org.kuali.kfs.module.cam.CamsKeyConstants; 031 import org.kuali.kfs.module.cam.CamsConstants.DocumentTypeName; 032 import org.kuali.kfs.module.cam.businessobject.AssetLock; 033 import org.kuali.kfs.module.cam.dataaccess.CapitalAssetLockDao; 034 import org.kuali.kfs.module.cam.service.AssetLockService; 035 import org.kuali.kfs.sys.KFSConstants; 036 import org.kuali.kfs.sys.context.SpringContext; 037 import org.kuali.rice.kns.service.BusinessObjectService; 038 import org.kuali.rice.kns.service.KualiConfigurationService; 039 import org.kuali.rice.kns.util.GlobalVariables; 040 import org.kuali.rice.kns.util.KNSConstants; 041 import org.kuali.rice.kns.util.RiceKeyConstants; 042 import org.kuali.rice.kns.util.UrlFactory; 043 import org.springframework.transaction.annotation.Transactional; 044 045 @Transactional 046 public class AssetLockServiceImpl implements AssetLockService { 047 private static Logger LOG = Logger.getLogger(AssetLockService.class); 048 049 private CapitalAssetLockDao capitalAssetLockDao; 050 051 // FP document types includes: 052 // CashReceipt,DistributionOfIncomeAndExpense,GeneralErrorCorrection,InternalBilling,ServiceBilling,YearEndDistributionOfIncomeAndExpense,YearEndGeneralErrorCorrection,ProcurementCard 053 private static final Map<String, String> FINANCIAL_DOC_TYPE_MAP = new HashMap<String, String>(); 054 static { 055 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.CASH_RECEIPT, KFSConstants.FinancialDocumentTypeCodes.CASH_RECEIPT); 056 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.ADVANCE_DEPOSIT, KFSConstants.FinancialDocumentTypeCodes.ADVANCE_DEPOSIT); 057 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.CREDIT_CARD_RECEIPT, KFSConstants.FinancialDocumentTypeCodes.CREDIT_CARD_RECEIPT); 058 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.DISTRIBUTION_OF_INCOME_AND_EXPENSE, KFSConstants.FinancialDocumentTypeCodes.DISTRIBUTION_OF_INCOME_AND_EXPENSE); 059 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.GENERAL_ERROR_CORRECTION, KFSConstants.FinancialDocumentTypeCodes.GENERAL_ERROR_CORRECTION); 060 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.INTERNAL_BILLING, KFSConstants.FinancialDocumentTypeCodes.INTERNAL_BILLING); 061 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.SERVICE_BILLING, KFSConstants.FinancialDocumentTypeCodes.SERVICE_BILLING); 062 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.YEAR_END_DISTRIBUTION_OF_INCOME_AND_EXPENSE, KFSConstants.FinancialDocumentTypeCodes.YEAR_END_DISTRIBUTION_OF_INCOME_AND_EXPENSE); 063 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.YEAR_END_GENERAL_ERROR_CORRECTION, KFSConstants.FinancialDocumentTypeCodes.YEAR_END_GENERAL_ERROR_CORRECTION); 064 FINANCIAL_DOC_TYPE_MAP.put(KFSConstants.FinancialDocumentTypeCodes.PROCUREMENT_CARD, KFSConstants.FinancialDocumentTypeCodes.PROCUREMENT_CARD); 065 } 066 067 // CAMS document types for maintain asset: AssetMaintenance, AssetFabrication, Asset Global, Asset Location Global, 068 // LoanAndReturn 069 private static final Map<String, String> ASSET_MAINTAIN_DOC_TYPE_MAP = new HashMap<String, String>(); 070 static { 071 ASSET_MAINTAIN_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_EDIT, DocumentTypeName.ASSET_EDIT); 072 ASSET_MAINTAIN_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_LOCATION_GLOBAL, DocumentTypeName.ASSET_LOCATION_GLOBAL); 073 ASSET_MAINTAIN_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_EQUIPMENT_LOAN_OR_RETURN, DocumentTypeName.ASSET_EQUIPMENT_LOAN_OR_RETURN); 074 ASSET_MAINTAIN_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_BARCODE_INVENTORY_ERROR, DocumentTypeName.ASSET_BARCODE_INVENTORY_ERROR); 075 ASSET_MAINTAIN_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_PAYMENT_FROM_CAB, DocumentTypeName.ASSET_PAYMENT_FROM_CAB); 076 } 077 078 // CAMS document types relating payment changes: AssetRetirement and Merge, AssetTransfer, AssetPayment, Asset Separate 079 private static final Map<String, String> ASSET_PMT_CHG_DOC_TYPE_MAP = new HashMap<String, String>(); 080 static { 081 ASSET_PMT_CHG_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_RETIREMENT_GLOBAL, DocumentTypeName.ASSET_RETIREMENT_GLOBAL); 082 ASSET_PMT_CHG_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_TRANSFER, DocumentTypeName.ASSET_TRANSFER); 083 ASSET_PMT_CHG_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_PAYMENT, DocumentTypeName.ASSET_PAYMENT); 084 ASSET_PMT_CHG_DOC_TYPE_MAP.put(DocumentTypeName.ASSET_SEPARATE, DocumentTypeName.ASSET_SEPARATE); 085 } 086 087 protected boolean isPurApDocument(String documentTypeName) { 088 return CabConstants.PREQ.equals(documentTypeName) || CabConstants.CM.equals(documentTypeName); 089 } 090 091 /** 092 * Gets the capitalAssetLockDao attribute. 093 * 094 * @return Returns the capitalAssetLockDao. 095 */ 096 public CapitalAssetLockDao getCapitalAssetLockDao() { 097 return capitalAssetLockDao; 098 } 099 100 101 /** 102 * Sets the capitalAssetLockDao attribute value. 103 * 104 * @param capitalAssetLockDao The capitalAssetLockDao to set. 105 */ 106 public void setCapitalAssetLockDao(CapitalAssetLockDao capitalAssetLockDao) { 107 this.capitalAssetLockDao = capitalAssetLockDao; 108 } 109 110 111 /** 112 * @param assetLocks must be from the same documentNumber and have the same documentTypeName 113 * @return Return false without any of the asset being locked. Return true when all assets can be locked. 114 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#checkAndLockForDocument(java.util.Collection) 115 */ 116 public synchronized boolean checkAndSetAssetLocks(List<AssetLock> assetLocks) { 117 if (assetLocks == null || assetLocks.isEmpty() || !assetLocks.iterator().hasNext()) { 118 return true; 119 } 120 121 AssetLock lock = assetLocks.iterator().next(); 122 String documentTypeName = lock.getDocumentTypeName(); 123 String documentNumber = lock.getDocumentNumber(); 124 125 // build asset number collection for lock checking. 126 List assetNumbers = new ArrayList<Long>(); 127 for (AssetLock assetLock : assetLocks) { 128 assetNumbers.add(assetLock.getCapitalAssetNumber()); 129 } 130 131 // check each assetNumber is not locked by other document(s). PurAp document will ignore the locks since CAB batch will 132 // set the lock anyway. 133 if (isAssetLocked(assetNumbers, documentTypeName, documentNumber)) { 134 return false; 135 } 136 // locking. here we got to delete and save again...it looks clumsy but it is constrained by the framework, see 137 // MaintenanceDocumentBase.postProcessSave() for example. 138 deleteAssetLocks(documentNumber, lock.getLockingInformation()); 139 getBusinessObjectService().save(assetLocks); 140 141 return true; 142 } 143 144 /** 145 * To get blocking document types for given document type. If given document type is FP, blocking documents will be CAMS payment 146 * change documents. If given document type is CAMs maintain related, the blocking documents are all CAMs doc excluding FP and 147 * PURAP. For other cases, returning null which will block all other documents. 148 * 149 * @param documentTypeName 150 * @return 151 */ 152 protected Collection getBlockingDocumentTypes(String documentTypeName) { 153 // FP document should be blocked by CAMS Payment change documents. 154 if (FINANCIAL_DOC_TYPE_MAP.containsKey(documentTypeName)) { 155 return ASSET_PMT_CHG_DOC_TYPE_MAP.values(); 156 } 157 // CAMS maintain docs 158 else if (ASSET_MAINTAIN_DOC_TYPE_MAP.containsKey(documentTypeName)) { 159 List financialDocTypes = new ArrayList<String>(); 160 financialDocTypes.addAll(ASSET_MAINTAIN_DOC_TYPE_MAP.values()); 161 financialDocTypes.addAll(ASSET_PMT_CHG_DOC_TYPE_MAP.values()); 162 return financialDocTypes; 163 } 164 // FP blocking documents 165 if (CamsConstants.DocumentTypeName.ASSET_FP_INQUIRY.equals(documentTypeName)) { 166 return FINANCIAL_DOC_TYPE_MAP.values(); 167 } 168 169 // PREQ blocking documents 170 if (CamsConstants.DocumentTypeName.ASSET_PREQ_INQUIRY.equals(documentTypeName)) { 171 List fpAndPurApDocTypes = new ArrayList<String>(); 172 fpAndPurApDocTypes.add(CabConstants.PREQ); 173 fpAndPurApDocTypes.add(CabConstants.CM); 174 return fpAndPurApDocTypes; 175 } 176 177 // For CAMs payment change document, any doc type can be the blocker, return null for this case 178 return null; 179 } 180 181 /** 182 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#deleteLocks(java.lang.String, java.lang.String) 183 */ 184 public void deleteAssetLocks(String documentNumber, String lockingInformation) { 185 if (StringUtils.isBlank(documentNumber)) { 186 return; 187 } 188 Map<String, Object> fieldValues = new HashMap<String, Object>(); 189 fieldValues.put(CabPropertyConstants.CapitalAssetLock.DOCUMENT_NUMBER, documentNumber); 190 if (StringUtils.isNotBlank(lockingInformation)) { 191 fieldValues.put(CabPropertyConstants.CapitalAssetLock.LOCKING_INFORMATION, lockingInformation); 192 } 193 getBusinessObjectService().deleteMatching(AssetLock.class, fieldValues); 194 } 195 196 /** 197 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#generateAssetLocks(java.util.Collection, 198 * java.lang.String, java.lang.String, java.lang.String) 199 */ 200 public List<AssetLock> buildAssetLockHelper(List<Long> assetNumbers, String documentNumber, String documentType, String lockingInformation) { 201 List<AssetLock> assetLocks = new ArrayList<AssetLock>(); 202 203 for (Long assetNumber : assetNumbers) { 204 if (assetNumber != null) { 205 AssetLock newLock = new AssetLock(documentNumber, assetNumber, lockingInformation, documentType); 206 assetLocks.add(newLock); 207 } 208 } 209 return assetLocks; 210 } 211 212 213 /** 214 * Generating error messages and doc links for blocking documents. 215 * 216 * @param blockingDocuments 217 */ 218 protected void addBlockingDocumentErrorMessage(Collection<String> blockingDocuments, String documentTypeName) { 219 for (String blockingDocId : blockingDocuments) { 220 // build the link URL for the blocking document. Better to use DocHandler because this could be 221 // a maintenance document or tDoc. 222 Properties parameters = new Properties(); 223 parameters.put(KNSConstants.PARAMETER_DOC_ID, blockingDocId); 224 parameters.put(KNSConstants.PARAMETER_COMMAND, KNSConstants.METHOD_DISPLAY_DOC_SEARCH_VIEW); 225 226 String blockingUrl = UrlFactory.parameterizeUrl(SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.WORKFLOW_URL_KEY) + "/" + KNSConstants.DOC_HANDLER_ACTION, parameters); 227 if (LOG.isDebugEnabled()) { 228 LOG.debug("blockingUrl = '" + blockingUrl + "'"); 229 LOG.debug("Record: " + blockingDocId + "is locked."); 230 } 231 232 // post an error about the locked document 233 String[] errorParameters = { blockingUrl, blockingDocId }; 234 if (FINANCIAL_DOC_TYPE_MAP.containsKey(documentTypeName)) { 235 // display a different error message for lock request from FP document. 236 GlobalVariables.getMessageMap().putError(KNSConstants.GLOBAL_ERRORS, CamsKeyConstants.AssetLock.ERROR_ASSET_LOCKED, errorParameters); 237 } 238 else { 239 GlobalVariables.getMessageMap().putError(KNSConstants.GLOBAL_ERRORS, CamsKeyConstants.AssetLock.ERROR_ASSET_MAINTENANCE_LOCKED, errorParameters); 240 } 241 } 242 } 243 244 protected BusinessObjectService getBusinessObjectService() { 245 return SpringContext.getBean(BusinessObjectService.class); 246 } 247 248 /** 249 * @see org.kuali.kfs.module.cam.service.AssetLockService#isAssetLockedByDocument(java.lang.String, java.lang.String) 250 */ 251 public boolean isAssetLockedByCurrentDocument(String documentNumber, String lockingInformation) { 252 if (StringUtils.isBlank(documentNumber)) { 253 return false; 254 } 255 Map<String, Object> fieldValues = new HashMap<String, Object>(); 256 fieldValues.put(CabPropertyConstants.CapitalAssetLock.DOCUMENT_NUMBER, documentNumber); 257 if (StringUtils.isNotBlank(lockingInformation)) { 258 fieldValues.put(CabPropertyConstants.CapitalAssetLock.LOCKING_INFORMATION, lockingInformation); 259 } 260 Collection<AssetLock> assetLocks = getBusinessObjectService().findMatching(AssetLock.class, fieldValues); 261 return assetLocks != null && !assetLocks.isEmpty(); 262 } 263 264 /** 265 * Based on the given documentTypeName, it decides what document types could block it. 266 * 267 * @see org.kuali.kfs.module.cam.service.AssetLockService#isAssetLocked(java.util.List, java.lang.String, java.lang.String) 268 */ 269 public boolean isAssetLocked(List<Long> assetNumbers, String documentTypeName, String excludingDocumentNumber) { 270 if (assetNumbers == null || assetNumbers.isEmpty()) { 271 return false; 272 } 273 if (!isPurApDocument(documentTypeName)) { 274 List<String> lockingDocumentNumbers = getAssetLockingDocuments(assetNumbers, documentTypeName, excludingDocumentNumber); 275 if (lockingDocumentNumbers != null && !lockingDocumentNumbers.isEmpty()) { 276 addBlockingDocumentErrorMessage(lockingDocumentNumbers, documentTypeName); 277 return true; 278 } 279 } 280 return false; 281 } 282 283 /** 284 * @see org.kuali.kfs.module.cam.service.AssetLockService#getAssetLockingDocuments(java.util.List, java.lang.String, 285 * java.lang.String) 286 */ 287 public List<String> getAssetLockingDocuments(List<Long> assetNumbers, String documentTypeName, String excludingDocumentNumber) { 288 Collection blockingDocumentTypes = getBlockingDocumentTypes(documentTypeName); 289 List<String> lockingDocumentNumbers = getCapitalAssetLockDao().getLockingDocumentNumbers(assetNumbers, blockingDocumentTypes, excludingDocumentNumber); 290 return lockingDocumentNumbers; 291 } 292 293 294 }