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 }