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.cab.service.impl; 017 018 import java.math.BigDecimal; 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.HashMap; 022 import java.util.HashSet; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.Map; 026 027 import org.apache.commons.lang.ArrayUtils; 028 import org.apache.commons.lang.StringUtils; 029 import org.apache.log4j.Logger; 030 import org.kuali.kfs.coa.businessobject.ObjectCode; 031 import org.kuali.kfs.fp.businessobject.CapitalAssetInformation; 032 import org.kuali.kfs.fp.businessobject.CapitalAssetInformationDetail; 033 import org.kuali.kfs.fp.document.AdvanceDepositDocument; 034 import org.kuali.kfs.fp.document.CashReceiptDocument; 035 import org.kuali.kfs.fp.document.CreditCardReceiptDocument; 036 import org.kuali.kfs.fp.document.DistributionOfIncomeAndExpenseDocument; 037 import org.kuali.kfs.fp.document.GeneralErrorCorrectionDocument; 038 import org.kuali.kfs.fp.document.InternalBillingDocument; 039 import org.kuali.kfs.fp.document.ProcurementCardDocument; 040 import org.kuali.kfs.fp.document.ServiceBillingDocument; 041 import org.kuali.kfs.fp.document.YearEndDistributionOfIncomeAndExpenseDocument; 042 import org.kuali.kfs.fp.document.YearEndGeneralErrorCorrectionDocument; 043 import org.kuali.kfs.integration.cab.CapitalAssetBuilderAssetTransactionType; 044 import org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService; 045 import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService; 046 import org.kuali.kfs.integration.purap.CapitalAssetLocation; 047 import org.kuali.kfs.integration.purap.CapitalAssetSystem; 048 import org.kuali.kfs.integration.purap.ExternalPurApItem; 049 import org.kuali.kfs.integration.purap.ItemCapitalAsset; 050 import org.kuali.kfs.integration.purap.PurchasingAccountsPayableModuleService; 051 import org.kuali.kfs.module.cab.CabConstants; 052 import org.kuali.kfs.module.cab.CabKeyConstants; 053 import org.kuali.kfs.module.cab.CabParameterConstants; 054 import org.kuali.kfs.module.cab.CabPropertyConstants; 055 import org.kuali.kfs.module.cab.businessobject.AssetTransactionType; 056 import org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntry; 057 import org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntryAsset; 058 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableDocument; 059 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableItemAsset; 060 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableLineAssetAccount; 061 import org.kuali.kfs.module.cab.document.service.PurApInfoService; 062 import org.kuali.kfs.module.cam.CamsConstants; 063 import org.kuali.kfs.module.cam.CamsKeyConstants; 064 import org.kuali.kfs.module.cam.CamsPropertyConstants; 065 import org.kuali.kfs.module.cam.CamsConstants.DocumentTypeName; 066 import org.kuali.kfs.module.cam.businessobject.Asset; 067 import org.kuali.kfs.module.cam.businessobject.AssetGlobal; 068 import org.kuali.kfs.module.cam.businessobject.AssetGlobalDetail; 069 import org.kuali.kfs.module.cam.businessobject.AssetPaymentAssetDetail; 070 import org.kuali.kfs.module.cam.businessobject.AssetType; 071 import org.kuali.kfs.module.cam.document.service.AssetService; 072 import org.kuali.kfs.module.purap.PurapConstants; 073 import org.kuali.kfs.module.purap.PurapKeyConstants; 074 import org.kuali.kfs.module.purap.PurapParameterConstants; 075 import org.kuali.kfs.module.purap.PurapPropertyConstants; 076 import org.kuali.kfs.module.purap.businessobject.AccountsPayableItem; 077 import org.kuali.kfs.module.purap.businessobject.AvailabilityMatrix; 078 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine; 079 import org.kuali.kfs.module.purap.businessobject.PurApItem; 080 import org.kuali.kfs.module.purap.businessobject.PurchasingCapitalAssetItem; 081 import org.kuali.kfs.module.purap.businessobject.PurchasingItem; 082 import org.kuali.kfs.module.purap.businessobject.PurchasingItemBase; 083 import org.kuali.kfs.module.purap.businessobject.PurchasingItemCapitalAssetBase; 084 import org.kuali.kfs.module.purap.document.AccountsPayableDocument; 085 import org.kuali.kfs.module.purap.document.PurchaseOrderDocument; 086 import org.kuali.kfs.module.purap.document.PurchasingDocument; 087 import org.kuali.kfs.module.purap.document.RequisitionDocument; 088 import org.kuali.kfs.sys.KFSConstants; 089 import org.kuali.kfs.sys.KFSKeyConstants; 090 import org.kuali.kfs.sys.KFSPropertyConstants; 091 import org.kuali.kfs.sys.businessobject.AccountingLine; 092 import org.kuali.kfs.sys.businessobject.Building; 093 import org.kuali.kfs.sys.businessobject.Room; 094 import org.kuali.kfs.sys.businessobject.SourceAccountingLine; 095 import org.kuali.kfs.sys.businessobject.TargetAccountingLine; 096 import org.kuali.kfs.sys.context.SpringContext; 097 import org.kuali.kfs.sys.document.AccountingDocument; 098 import org.kuali.kfs.sys.service.impl.KfsParameterConstants; 099 import org.kuali.rice.kns.bo.Campus; 100 import org.kuali.rice.kns.bo.DocumentHeader; 101 import org.kuali.rice.kns.bo.Parameter; 102 import org.kuali.rice.kns.datadictionary.AttributeDefinition; 103 import org.kuali.rice.kns.datadictionary.BusinessObjectEntry; 104 import org.kuali.rice.kns.service.BusinessObjectDictionaryService; 105 import org.kuali.rice.kns.service.BusinessObjectService; 106 import org.kuali.rice.kns.service.DataDictionaryService; 107 import org.kuali.rice.kns.service.DictionaryValidationService; 108 import org.kuali.rice.kns.service.KualiConfigurationService; 109 import org.kuali.rice.kns.service.KualiModuleService; 110 import org.kuali.rice.kns.service.ParameterService; 111 import org.kuali.rice.kns.util.GlobalVariables; 112 import org.kuali.rice.kns.util.KualiDecimal; 113 import org.kuali.rice.kns.util.ObjectUtils; 114 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 115 116 public class CapitalAssetBuilderModuleServiceImpl implements CapitalAssetBuilderModuleService { 117 private static Logger LOG = Logger.getLogger(CapitalAssetBuilderModuleService.class); 118 119 protected static enum AccountCapitalObjectCode { 120 BOTH_NONCAP { 121 boolean validateAssetInfoAllowed(AccountingDocument accountingDocument, boolean isNewAssetBlank, boolean isUpdateAssetBlank) { 122 boolean valid = true; 123 // non capital object code, disallow the capital information entered. 124 if (!isNewAssetBlank || !isUpdateAssetBlank) { 125 // give error if data was entered 126 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, CabKeyConstants.CapitalAssetInformation.ERROR_ASSET_DO_NOT_ENTER_ANY_DATA); 127 valid = false; 128 } 129 130 return valid; 131 } 132 133 boolean isDocumentTypeRestricted(AccountingDocument accountingDocument) { 134 return false; 135 } 136 }, 137 FROM_CAPITAL_TO_NONCAP { 138 boolean validateAssetInfoAllowed(AccountingDocument accountingDocument, boolean isNewAssetBlank, boolean isUpdateAssetBlank) { 139 boolean valid = validateAssetInfoEntered(isNewAssetBlank, isUpdateAssetBlank); 140 if (valid) { 141 if (isDocumentTypeRestricted(accountingDocument)) { 142 valid &= validateOnlyOneAssetInfoEntered(isNewAssetBlank, isUpdateAssetBlank); 143 } 144 // Capital on the FROM side and non-capital on the TO side, we only allow modify an asset. 145 else if (!isNewAssetBlank || isUpdateAssetBlank) { 146 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, CabKeyConstants.CapitalAssetInformation.ERROR_ASSET_UPDATE_ALLOW_ONLY); 147 valid = false; 148 } 149 } 150 return valid; 151 } 152 153 /** 154 * For Internal Billing, when credit capital on Income line, allow modify asset or create new asset. 155 * 156 * @see org.kuali.kfs.module.cab.service.impl.CapitalAssetBuilderModuleServiceImpl.AccountCapitalObjectCode#isDocumentTypeRestricted(org.kuali.kfs.sys.document.AccountingDocument) 157 */ 158 boolean isDocumentTypeRestricted(AccountingDocument accountingDocument) { 159 if (accountingDocument instanceof InternalBillingDocument) { 160 return true; 161 } 162 return false; 163 } 164 }, 165 FROM_NONCAP_TO_CAPITAL { 166 boolean validateAssetInfoAllowed(AccountingDocument accountingDocument, boolean isNewAssetBlank, boolean isUpdateAssetBlank) { 167 boolean valid = validateAssetInfoEntered(isNewAssetBlank, isUpdateAssetBlank); 168 if (valid && isDocumentTypeRestricted(accountingDocument)) { 169 valid &= validateOnlyOneAssetInfoEntered(isNewAssetBlank, isUpdateAssetBlank); 170 } 171 return valid; 172 } 173 174 /** 175 * The document is restricted for all FP doc types 176 * 177 * @see org.kuali.kfs.module.cab.service.impl.CapitalAssetBuilderModuleServiceImpl.AccountCapitalObjectCode#isDocumentTypeRestricted(org.kuali.kfs.sys.document.AccountingDocument) 178 */ 179 boolean isDocumentTypeRestricted(AccountingDocument accountingDocument) { 180 return true; 181 } 182 }, 183 BOTH_CAPITAL { 184 boolean validateAssetInfoAllowed(AccountingDocument accountingDocument, boolean isNewAssetBlank, boolean isUpdateAssetBlank) { 185 return validateAssetInfoEntered(isNewAssetBlank, isUpdateAssetBlank) && validateOnlyOneAssetInfoEntered(isNewAssetBlank, isUpdateAssetBlank); 186 } 187 188 boolean isDocumentTypeRestricted(AccountingDocument accountingDocument) { 189 return false; 190 } 191 }; 192 193 /** 194 * Validate Asset Information is not blank. 195 * 196 * @param isNewAssetBlank 197 * @param isUpdateAssetBlank 198 * @return 199 */ 200 protected static boolean validateAssetInfoEntered(boolean isNewAssetBlank, boolean isUpdateAssetBlank) { 201 boolean valid = true; 202 // can modify existing or create new. Required to enter one of each type. 203 if (isNewAssetBlank && isUpdateAssetBlank) { 204 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, CabKeyConstants.CapitalAssetInformation.ERROR_ASSET_REQUIRE_DATA_ENTRY); 205 valid = false; 206 } 207 return valid; 208 } 209 210 /** 211 * Validate only either one asset information entered. 212 * 213 * @param isNewAssetBlank 214 * @param isUpdateAssetBlank 215 * @return 216 */ 217 protected static boolean validateOnlyOneAssetInfoEntered(boolean isNewAssetBlank, boolean isUpdateAssetBlank) { 218 boolean valid = true; 219 if (!isNewAssetBlank && !isUpdateAssetBlank) { 220 // Data exists on both crate new asset and update asset, give error 221 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, CabKeyConstants.CapitalAssetInformation.ERROR_ASSET_NEW_OR_UPDATE_ONLY); 222 valid = false; 223 } 224 return valid; 225 } 226 227 abstract boolean validateAssetInfoAllowed(AccountingDocument accoutingDocument, boolean isNewAssetBlank, boolean isUpdateAssetBlank); 228 229 abstract boolean isDocumentTypeRestricted(AccountingDocument accountingDocument); 230 } 231 232 /** 233 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#getAllAssetTransactionTypes() 234 */ 235 public List<CapitalAssetBuilderAssetTransactionType> getAllAssetTransactionTypes() { 236 Class<? extends CapitalAssetBuilderAssetTransactionType> assetTransactionTypeClass = this.getKualiModuleService().getResponsibleModuleService(CapitalAssetBuilderAssetTransactionType.class).getExternalizableBusinessObjectImplementation(CapitalAssetBuilderAssetTransactionType.class); 237 Map<String, Object> searchKeys = new HashMap<String, Object>(); 238 searchKeys.put("active", "Y"); 239 return (List<CapitalAssetBuilderAssetTransactionType>) this.getBusinessObjectService().findMatching(assetTransactionTypeClass, searchKeys); 240 } 241 242 /** 243 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#validatePurchasingAccountsPayableData(org.kuali.kfs.sys.document.AccountingDocument) 244 */ 245 public boolean validatePurchasingData(AccountingDocument accountingDocument) { 246 Boolean valid = true; 247 PurchasingDocument purchasingDocument = (PurchasingDocument) accountingDocument; 248 String systemTypeCode = purchasingDocument.getCapitalAssetSystemTypeCode(); 249 String capitalAssetSystemStateCode = purchasingDocument.getCapitalAssetSystemStateCode(); 250 String documentType = (purchasingDocument instanceof RequisitionDocument) ? "REQUISITION" : "PURCHASE_ORDER"; 251 for (PurApItem item : purchasingDocument.getItems()) { 252 List accountingLines = item.getSourceAccountingLines(); 253 for (Iterator iterator = accountingLines.iterator(); iterator.hasNext();) { 254 PurApAccountingLine accountingLine = (PurApAccountingLine) iterator.next(); 255 String coa = accountingLine.getChartOfAccountsCode(); 256 if (PurapConstants.CapitalAssetSystemTypes.INDIVIDUAL.equals(systemTypeCode)) { 257 valid &= validateIndividualCapitalAssetSystemFromPurchasing(capitalAssetSystemStateCode, purchasingDocument.getPurchasingCapitalAssetItems(), coa, documentType); 258 } 259 else if (PurapConstants.CapitalAssetSystemTypes.ONE_SYSTEM.equals(systemTypeCode)) { 260 valid &= validateOneSystemCapitalAssetSystemFromPurchasing(capitalAssetSystemStateCode, purchasingDocument.getPurchasingCapitalAssetSystems(), purchasingDocument.getPurchasingCapitalAssetItems(), coa, documentType); 261 } 262 else if (PurapConstants.CapitalAssetSystemTypes.MULTIPLE.equals(systemTypeCode)) { 263 valid &= validateMultipleSystemsCapitalAssetSystemFromPurchasing(capitalAssetSystemStateCode, purchasingDocument.getPurchasingCapitalAssetSystems(), purchasingDocument.getPurchasingCapitalAssetItems(), coa, documentType); 264 } 265 } 266 } 267 return valid; 268 } 269 270 public boolean validateAccountsPayableData(AccountingDocument accountingDocument) { 271 AccountsPayableDocument apDocument = (AccountsPayableDocument) accountingDocument; 272 boolean valid = true; 273 for (PurApItem purApItem : apDocument.getItems()) { 274 AccountsPayableItem accountsPayableItem = (AccountsPayableItem) purApItem; 275 // only run on ap items that were line items (not additional charge items) and were cams items 276 if ((!accountsPayableItem.getItemType().isAdditionalChargeIndicator()) && StringUtils.isNotEmpty(accountsPayableItem.getCapitalAssetTransactionTypeCode())) { 277 valid &= validateAccountsPayableItem(accountsPayableItem); 278 } 279 } 280 return valid; 281 } 282 283 /** 284 * Perform the item level capital asset validation to determine if the given document is not allowed to become an Automatic 285 * Purchase Order (APO). The APO is not allowed if any accounting strings on the document are using an object level indicated as 286 * capital via a parameter setting. 287 */ 288 public boolean doesAccountingLineFailAutomaticPurchaseOrderRules(AccountingLine accountingLine) { 289 PurApAccountingLine purapAccountingLine = (PurApAccountingLine) accountingLine; 290 purapAccountingLine.refreshNonUpdateableReferences(); 291 return getParameterService().getParameterEvaluator(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, CabParameterConstants.CapitalAsset.CAPITAL_ASSET_OBJECT_LEVELS, purapAccountingLine.getObjectCode().getFinancialObjectLevelCode()).evaluationSucceeds(); 292 } 293 294 /** 295 * Perform the document level capital asset validation to determine if the given document is not allowed to become an Automatic 296 * Purchase Order (APO). The APO is not allowed if any capital asset items exist on the document. 297 */ 298 public boolean doesDocumentFailAutomaticPurchaseOrderRules(AccountingDocument accountingDocument) { 299 PurchasingDocument purchasingDocument = (PurchasingDocument) accountingDocument; 300 return ObjectUtils.isNotNull(purchasingDocument.getPurchasingCapitalAssetItems()) && !purchasingDocument.getPurchasingCapitalAssetItems().isEmpty(); 301 } 302 303 public boolean validateAutomaticPurchaseOrderRule(AccountingDocument accountingDocument) { 304 PurchasingDocument purchasingDocument = (PurchasingDocument) accountingDocument; 305 for (PurApItem item : purchasingDocument.getItems()) { 306 if (doesItemNeedCapitalAsset(item.getItemTypeCode(), item.getSourceAccountingLines())) { 307 // if the item needs capital asset, we cannot have an APO, so return false. 308 return false; 309 } 310 } 311 return true; 312 } 313 314 /** 315 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#doesItemNeedCapitalAsset(java.lang.String, java.util.List) 316 */ 317 public boolean doesItemNeedCapitalAsset(String itemTypeCode, List accountingLines) { 318 if (PurapConstants.ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE.equals(itemTypeCode)) { 319 // FIXME: Chris - this should be true but need to look to see where itemline number is referenced first 320 // return true; 321 return false; 322 }// else 323 for (Iterator iterator = accountingLines.iterator(); iterator.hasNext();) { 324 PurApAccountingLine accountingLine = (PurApAccountingLine) iterator.next(); 325 accountingLine.refreshReferenceObject(KFSPropertyConstants.OBJECT_CODE); 326 if (ObjectUtils.isNotNull(accountingLine.getObjectCode()) && isCapitalAssetObjectCode(accountingLine.getObjectCode())) { 327 return true; 328 } 329 } 330 331 return false; 332 } 333 334 /** 335 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#validateUpdateCAMSView(org.kuali.kfs.sys.document.AccountingDocument) 336 */ 337 public boolean validateUpdateCAMSView(AccountingDocument accountingDocument) { 338 PurchasingDocument purchasingDocument = (PurchasingDocument) accountingDocument; 339 boolean valid = true; 340 for (PurApItem purapItem : purchasingDocument.getItems()) { 341 if (purapItem.getItemType().isLineItemIndicator()) { 342 if (!doesItemNeedCapitalAsset(purapItem.getItemTypeCode(), purapItem.getSourceAccountingLines())) { 343 PurchasingCapitalAssetItem camsItem = ((PurchasingItem) purapItem).getPurchasingCapitalAssetItem(); 344 if (camsItem != null && !camsItem.isEmpty()) { 345 valid = false; 346 GlobalVariables.getMessageMap().putError("newPurchasingItemCapitalAssetLine", PurapKeyConstants.ERROR_CAPITAL_ASSET_ITEM_NOT_CAMS_ELIGIBLE, "in line item # " + purapItem.getItemLineNumber()); 347 } 348 } 349 } 350 } 351 return valid; 352 } 353 354 /** 355 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#validateAddItemCapitalAssetBusinessRules(org.kuali.kfs.integration.purap.ItemCapitalAsset) 356 */ 357 public boolean validateAddItemCapitalAssetBusinessRules(ItemCapitalAsset asset) { 358 boolean valid = true; 359 if (asset.getCapitalAssetNumber() == null) { 360 valid = false; 361 } 362 else { 363 valid = SpringContext.getBean(DictionaryValidationService.class).isBusinessObjectValid((PurchasingItemCapitalAssetBase) asset); 364 } 365 if (!valid) { 366 String propertyName = "newPurchasingItemCapitalAssetLine." + PurapPropertyConstants.CAPITAL_ASSET_NUMBER; 367 String errorKey = PurapKeyConstants.ERROR_CAPITAL_ASSET_ASSET_NUMBER_MUST_BE_LONG_NOT_NULL; 368 GlobalVariables.getMessageMap().putError(propertyName, errorKey); 369 }else{ 370 Map<String, String> params = new HashMap<String, String>(); 371 params.put(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, asset.getCapitalAssetNumber().toString()); 372 Asset retrievedAsset = (Asset) this.getBusinessObjectService().findByPrimaryKey(Asset.class, params); 373 374 if (ObjectUtils.isNull(retrievedAsset)) { 375 valid = false; 376 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, KFSPropertyConstants.CAPITAL_ASSET_NUMBER); 377 GlobalVariables.getMessageMap().putError("newPurchasingItemCapitalAssetLine." + KFSPropertyConstants.CAPITAL_ASSET_NUMBER, KFSKeyConstants.ERROR_EXISTENCE, label); 378 } 379 380 } 381 return valid; 382 } 383 384 /** 385 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#warningObjectLevelCapital(org.kuali.kfs.sys.document.AccountingDocument) 386 */ 387 public boolean warningObjectLevelCapital(AccountingDocument accountingDocument) { 388 org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument purapDocument = (org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument) accountingDocument; 389 for (PurApItem item : purapDocument.getItems()) { 390 if (item.getItemType().isLineItemIndicator() && item.getItemType().isQuantityBasedGeneralLedgerIndicator()) { 391 List<PurApAccountingLine> accounts = item.getSourceAccountingLines(); 392 BigDecimal unitPrice = item.getItemUnitPrice(); 393 String itemIdentifier = item.getItemIdentifierString(); 394 for (PurApAccountingLine account : accounts) { 395 ObjectCode objectCode = account.getObjectCode(); 396 if (!validateLevelCapitalAssetIndication(unitPrice, objectCode, itemIdentifier)) { 397 // found an error 398 return false; 399 } 400 } 401 } 402 } 403 // no need for error 404 return true; 405 } 406 407 408 /** 409 * Validates the capital asset field requirements based on system parameter and chart for individual system type. This also 410 * calls validations for quantity on locations equal quantity on line items, validates that the transaction type allows asset 411 * number and validates the non quantity driven allowed indicator. 412 * 413 * @param systemState 414 * @param capitalAssetItems 415 * @param chartCode 416 * @param documentType 417 * @return 418 */ 419 protected boolean validateIndividualCapitalAssetSystemFromPurchasing(String systemState, List<PurchasingCapitalAssetItem> capitalAssetItems, String chartCode, String documentType) { 420 // For Individual Asset system type, the List of CapitalAssetSystems in the input parameter for 421 // validateAllFieldRequirementsByChart 422 // should be null. So we'll pass in a null here. 423 boolean valid = validateAllFieldRequirementsByChart(systemState, null, capitalAssetItems, chartCode, documentType, PurapConstants.CapitalAssetSystemTypes.INDIVIDUAL); 424 valid &= validateQuantityOnLocationsEqualsQuantityOnItem(capitalAssetItems, PurapConstants.CapitalAssetSystemTypes.INDIVIDUAL, systemState); 425 valid &= validateIndividualSystemPurchasingTransactionTypesAllowingAssetNumbers(capitalAssetItems); 426 valid &= validateNonQuantityDrivenAllowedIndicatorAndTradeIn(capitalAssetItems); 427 return valid; 428 } 429 430 /** 431 * Validates the capital asset field requirements based on system parameter and chart for one system type. This also calls 432 * validations that the transaction type allows asset number and validates the non quantity driven allowed indicator. 433 * 434 * @param systemState 435 * @param capitalAssetSystems 436 * @param capitalAssetItems 437 * @param chartCode 438 * @param documentType 439 * @return 440 */ 441 protected boolean validateOneSystemCapitalAssetSystemFromPurchasing(String systemState, List<CapitalAssetSystem> capitalAssetSystems, List<PurchasingCapitalAssetItem> capitalAssetItems, String chartCode, String documentType) { 442 boolean valid = validateAllFieldRequirementsByChart(systemState, capitalAssetSystems, capitalAssetItems, chartCode, documentType, PurapConstants.CapitalAssetSystemTypes.ONE_SYSTEM); 443 String capitalAssetTransactionType = capitalAssetItems.get(0).getCapitalAssetTransactionTypeCode(); 444 String prefix = "document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_SYSTEMS + "[0]."; 445 valid &= validatePurchasingTransactionTypesAllowingAssetNumbers(capitalAssetSystems.get(0), capitalAssetTransactionType, prefix); 446 valid &= validateNonQuantityDrivenAllowedIndicatorAndTradeIn(capitalAssetItems); 447 return valid; 448 } 449 450 /** 451 * Validates the capital asset field requirements based on system parameter and chart for multiple system type. This also calls 452 * validations that the transaction type allows asset number and validates the non quantity driven allowed indicator. 453 * 454 * @param systemState 455 * @param capitalAssetSystems 456 * @param capitalAssetItems 457 * @param chartCode 458 * @param documentType 459 * @return 460 */ 461 protected boolean validateMultipleSystemsCapitalAssetSystemFromPurchasing(String systemState, List<CapitalAssetSystem> capitalAssetSystems, List<PurchasingCapitalAssetItem> capitalAssetItems, String chartCode, String documentType) { 462 boolean valid = validateAllFieldRequirementsByChart(systemState, capitalAssetSystems, capitalAssetItems, chartCode, documentType, PurapConstants.CapitalAssetSystemTypes.MULTIPLE); 463 String capitalAssetTransactionType = capitalAssetItems.get(0).getCapitalAssetTransactionTypeCode(); 464 String prefix = "document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_SYSTEMS + "[0]."; 465 valid &= validatePurchasingTransactionTypesAllowingAssetNumbers(capitalAssetSystems.get(0), capitalAssetTransactionType, prefix); 466 valid &= validateNonQuantityDrivenAllowedIndicatorAndTradeIn(capitalAssetItems); 467 return valid; 468 } 469 470 /** 471 * Validates all the field requirements by chart. It obtains a List of parameters where the parameter names are like 472 * "CHARTS_REQUIRING%" then loop through these parameters. If the system type is individual then invoke the 473 * validateFieldRequirementByChartForIndividualSystemType for further validation, otherwise invoke the 474 * validateFieldRequirementByChartForOneOrMultipleSystemType for further validation. 475 * 476 * @param systemState 477 * @param capitalAssetSystems 478 * @param capitalAssetItems 479 * @param chartCode 480 * @param documentType 481 * @param systemType 482 * @return 483 */ 484 protected boolean validateAllFieldRequirementsByChart(String systemState, List<CapitalAssetSystem> capitalAssetSystems, List<PurchasingCapitalAssetItem> capitalAssetItems, String chartCode, String documentType, String systemType) { 485 boolean valid = true; 486 List<Parameter> results = new ArrayList<Parameter>(); 487 Map<String, String> criteria = new HashMap<String, String>(); 488 criteria.put(CabPropertyConstants.Parameter.PARAMETER_NAMESPACE_CODE, CabConstants.Parameters.NAMESPACE); 489 criteria.put(CabPropertyConstants.Parameter.PARAMETER_DETAIL_TYPE_CODE, CabConstants.Parameters.DETAIL_TYPE_DOCUMENT); 490 criteria.put(CabPropertyConstants.Parameter.PARAMETER_NAME, "CHARTS_REQUIRING%" + documentType); 491 results.addAll(SpringContext.getBean(ParameterService.class).retrieveParametersGivenLookupCriteria(criteria)); 492 for (Parameter parameter : results) { 493 if (ObjectUtils.isNotNull(parameter)) { 494 if (systemType.equals(PurapConstants.CapitalAssetSystemTypes.INDIVIDUAL)) { 495 valid &= validateFieldRequirementByChartForIndividualSystemType(systemState, capitalAssetItems, chartCode, parameter.getParameterName(), parameter.getParameterValue()); 496 } 497 else { 498 valid &= validateFieldRequirementByChartForOneOrMultipleSystemType(systemType, systemState, capitalAssetSystems, capitalAssetItems, chartCode, parameter.getParameterName(), parameter.getParameterValue()); 499 } 500 } 501 } 502 return valid; 503 } 504 505 /** 506 * Validates all the field requirements by chart. It obtains a List of parameters where the parameter names are like 507 * "CHARTS_REQUIRING%" then loop through these parameters. If any of the parameter's values is null, then return false 508 * 509 * @param accountingDocument 510 * @return 511 */ 512 public boolean validateAllFieldRequirementsByChart(AccountingDocument accountingDocument) { 513 PurchasingDocument purchasingDocument = (PurchasingDocument) accountingDocument; 514 515 String documentType = (purchasingDocument instanceof RequisitionDocument) ? "REQUISITION" : "PURCHASE_ORDER"; 516 boolean valid = true; 517 518 for (PurApItem item : purchasingDocument.getItems()) { 519 String itemTypeCode = item.getItemTypeCode(); 520 List accountingLines = item.getSourceAccountingLines(); 521 for (Iterator iterator = accountingLines.iterator(); iterator.hasNext();) { 522 PurApAccountingLine accountingLine = (PurApAccountingLine) iterator.next(); 523 String coa = accountingLine.getChartOfAccountsCode(); 524 List<Parameter> results = new ArrayList<Parameter>(); 525 Map<String, String> criteria = new HashMap<String, String>(); 526 criteria.put(CabPropertyConstants.Parameter.PARAMETER_NAMESPACE_CODE, CabConstants.Parameters.NAMESPACE); 527 criteria.put(CabPropertyConstants.Parameter.PARAMETER_DETAIL_TYPE_CODE, CabConstants.Parameters.DETAIL_TYPE_DOCUMENT); 528 criteria.put(CabPropertyConstants.Parameter.PARAMETER_NAME, "CHARTS_REQUIRING%" + documentType); 529 criteria.put(CabPropertyConstants.Parameter.PARAMETER_VALUE, "%" + coa + "%"); 530 results.addAll(SpringContext.getBean(ParameterService.class).retrieveParametersGivenLookupCriteria(criteria)); 531 for (Parameter parameter : results) { 532 if (ObjectUtils.isNotNull(parameter)) { 533 if (parameter.getParameterValue() != null) { 534 return false; 535 } 536 } 537 } 538 } 539 } 540 return valid; 541 } 542 543 /** 544 * Validates for PURCHASING_OBJECT_SUB_TYPES parameter. If at least one object code of any accounting line entered is of this 545 * type, then return false. 546 * 547 * @param accountingDocument 548 * @return 549 */ 550 public boolean validatePurchasingObjectSubType(AccountingDocument accountingDocument) { 551 boolean valid = true; 552 PurchasingDocument purchasingDocument = (PurchasingDocument) accountingDocument; 553 for (PurApItem item : purchasingDocument.getItems()) { 554 String itemTypeCode = item.getItemTypeCode(); 555 List accountingLines = item.getSourceAccountingLines(); 556 for (Iterator iterator = accountingLines.iterator(); iterator.hasNext();) { 557 PurApAccountingLine accountingLine = (PurApAccountingLine) iterator.next(); 558 accountingLine.refreshReferenceObject(KFSPropertyConstants.OBJECT_CODE); 559 if (ObjectUtils.isNotNull(accountingLine.getObjectCode()) && isCapitalAssetObjectCode(accountingLine.getObjectCode())) { 560 return false; 561 } 562 } 563 } 564 return valid; 565 } 566 567 /** 568 * Validates field requirement by chart for one or multiple system types. 569 * 570 * @param systemType 571 * @param systemState 572 * @param capitalAssetSystems 573 * @param capitalAssetItems 574 * @param chartCode 575 * @param parameterName 576 * @param parameterValueString 577 * @return 578 */ 579 protected boolean validateFieldRequirementByChartForOneOrMultipleSystemType(String systemType, String systemState, List<CapitalAssetSystem> capitalAssetSystems, List<PurchasingCapitalAssetItem> capitalAssetItems, String chartCode, String parameterName, String parameterValueString) { 580 boolean valid = true; 581 boolean needValidation = (this.getParameterService().getParameterEvaluator(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, parameterName, chartCode).evaluationSucceeds()); 582 583 if (needValidation) { 584 if (parameterName.startsWith("CHARTS_REQUIRING_LOCATIONS_ADDRESS")) { 585 return validateCapitalAssetLocationAddressFieldsOneOrMultipleSystemType(capitalAssetSystems); 586 } 587 String mappedName = PurapConstants.CAMS_REQUIREDNESS_FIELDS.REQUIREDNESS_FIELDS_BY_PARAMETER_NAMES.get(parameterName); 588 if (mappedName != null) { 589 // Check the availability matrix here, if this field doesn't exist according to the avail. matrix, then no need 590 // to validate any further. 591 String availableValue = getValueFromAvailabilityMatrix(mappedName, systemType, systemState); 592 if (availableValue.equals(PurapConstants.CapitalAssetAvailability.NONE)) { 593 return true; 594 } 595 596 // capitalAssetTransactionType field is off the item 597 if (mappedName.equals(PurapPropertyConstants.CAPITAL_ASSET_TRANSACTION_TYPE_CODE)) { 598 String[] mappedNames = { PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS, mappedName }; 599 600 for (PurchasingCapitalAssetItem item : capitalAssetItems) { 601 StringBuffer keyBuffer = new StringBuffer("document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS + "[" + new Integer(item.getPurchasingItem().getItemLineNumber().intValue() - 1) + "]."); 602 valid &= validateFieldRequirementByChartHelper(item, ArrayUtils.subarray(mappedNames, 1, mappedNames.length), keyBuffer, item.getPurchasingItem().getItemLineNumber()); 603 } 604 } 605 // all the other fields are off the system. 606 else { 607 List<String> mappedNamesList = new ArrayList<String>(); 608 mappedNamesList.add(PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_SYSTEMS); 609 if (mappedName.indexOf(".") < 0) { 610 mappedNamesList.add(mappedName); 611 } 612 else { 613 mappedNamesList.addAll(mappedNameSplitter(mappedName)); 614 } 615 // For One system type, we would only have 1 CapitalAssetSystem, however, for multiple we may have more than 616 // one systems in the future. Either way, this for loop should allow both the one system and multiple system 617 // types to work fine. 618 int count = 0; 619 for (CapitalAssetSystem system : capitalAssetSystems) { 620 StringBuffer keyBuffer = new StringBuffer("document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_SYSTEMS + "[" + new Integer(count) + "]."); 621 valid &= validateFieldRequirementByChartHelper(system, ArrayUtils.subarray(mappedNamesList.toArray(), 1, mappedNamesList.size()), keyBuffer, null); 622 count++; 623 } 624 } 625 } 626 } 627 return valid; 628 } 629 630 /** 631 * Validates the field requirement by chart for individual system type. 632 * 633 * @param systemState 634 * @param capitalAssetItems 635 * @param chartCode 636 * @param parameterName 637 * @param parameterValueString 638 * @return 639 */ 640 protected boolean validateFieldRequirementByChartForIndividualSystemType(String systemState, List<PurchasingCapitalAssetItem> capitalAssetItems, String chartCode, String parameterName, String parameterValueString) { 641 boolean valid = true; 642 boolean needValidation = (this.getParameterService().getParameterEvaluator(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, parameterName, chartCode).evaluationSucceeds()); 643 644 if (needValidation) { 645 if (parameterName.startsWith("CHARTS_REQUIRING_LOCATIONS_ADDRESS")) { 646 return validateCapitalAssetLocationAddressFieldsForIndividualSystemType(capitalAssetItems); 647 } 648 String mappedName = PurapConstants.CAMS_REQUIREDNESS_FIELDS.REQUIREDNESS_FIELDS_BY_PARAMETER_NAMES.get(parameterName); 649 if (mappedName != null) { 650 // Check the availability matrix here, if this field doesn't exist according to the avail. matrix, then no need 651 // to validate any further. 652 String availableValue = getValueFromAvailabilityMatrix(mappedName, PurapConstants.CapitalAssetSystemTypes.INDIVIDUAL, systemState); 653 if (availableValue.equals(PurapConstants.CapitalAssetAvailability.NONE)) { 654 return true; 655 } 656 657 // capitalAssetTransactionType field is off the item 658 List<String> mappedNamesList = new ArrayList<String>(); 659 660 if (mappedName.equals(PurapPropertyConstants.CAPITAL_ASSET_TRANSACTION_TYPE_CODE)) { 661 mappedNamesList.add(PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS); 662 mappedNamesList.add(mappedName); 663 664 } 665 // all the other fields are off the system which is off the item 666 else { 667 mappedNamesList.add(PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS); 668 mappedNamesList.add(PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_SYSTEM); 669 if (mappedName.indexOf(".") < 0) { 670 mappedNamesList.add(mappedName); 671 } 672 else { 673 mappedNamesList.addAll(mappedNameSplitter(mappedName)); 674 } 675 } 676 // For Individual system type, we'll always iterate through the item, then if the field is off the system, we'll get 677 // it through 678 // the purchasingCapitalAssetSystem of the item. 679 for (PurchasingCapitalAssetItem item : capitalAssetItems) { 680 StringBuffer keyBuffer = new StringBuffer("document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS + "[" + new Integer(item.getPurchasingItem().getItemLineNumber().intValue() - 1) + "]."); 681 valid &= validateFieldRequirementByChartHelper(item, ArrayUtils.subarray(mappedNamesList.toArray(), 1, mappedNamesList.size()), keyBuffer, item.getPurchasingItem().getItemLineNumber()); 682 } 683 } 684 } 685 return valid; 686 } 687 688 /** 689 * Utility method to split a long String using the "." as the delimiter then add each of the array element into a List of String 690 * and return the List of String. 691 * 692 * @param mappedName The String to be splitted. 693 * @return The List of String after being splitted, with the "." as delimiter. 694 */ 695 protected List<String> mappedNameSplitter(String mappedName) { 696 List<String> result = new ArrayList<String>(); 697 String[] mappedNamesArray = mappedName.split("\\."); 698 for (int i = 0; i < mappedNamesArray.length; i++) { 699 result.add(mappedNamesArray[i]); 700 } 701 return result; 702 } 703 704 /** 705 * Validates the field requirement by chart recursively and give error messages when it returns false. 706 * 707 * @param bean The object to be used to obtain the property value 708 * @param mappedNames The array of Strings which when combined together, they form the field property 709 * @param errorKey The error key to be used for adding error messages to the error map. 710 * @param itemNumber The Integer that represents the item number that we're currently iterating. 711 * @return true if it passes the validation. 712 */ 713 protected boolean validateFieldRequirementByChartHelper(Object bean, Object[] mappedNames, StringBuffer errorKey, Integer itemNumber) { 714 boolean valid = true; 715 Object value = ObjectUtils.getPropertyValue(bean, (String) mappedNames[0]); 716 if (ObjectUtils.isNull(value)) { 717 errorKey.append(mappedNames[0]); 718 String fieldName = SpringContext.getBean(DataDictionaryService.class).getAttributeErrorLabel(bean.getClass(), (String) mappedNames[0]); 719 if (itemNumber != null) { 720 fieldName = fieldName + " in Item " + itemNumber; 721 } 722 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, fieldName); 723 return false; 724 } 725 else if (value instanceof Collection) { 726 if (((Collection) value).isEmpty()) { 727 // if this collection doesn't contain anything, when it's supposed to contain some strings with values, return 728 // false. 729 errorKey.append(mappedNames[0]); 730 String mappedNameStr = (String) mappedNames[0]; 731 String methodToInvoke = "get" + (mappedNameStr.substring(0, 1)).toUpperCase() + mappedNameStr.substring(1, mappedNameStr.length() - 1) + "Class"; 732 Class offendingClass; 733 try { 734 offendingClass = (Class) bean.getClass().getMethod(methodToInvoke, (Class[])null).invoke(bean, (Object[])null); 735 } 736 catch (Exception ex) { 737 throw new RuntimeException(ex); 738 } 739 BusinessObjectEntry boe = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(offendingClass.getSimpleName()); 740 List<AttributeDefinition> offendingAttributes = boe.getAttributes(); 741 AttributeDefinition offendingAttribute = offendingAttributes.get(0); 742 String fieldName = SpringContext.getBean(DataDictionaryService.class).getAttributeShortLabel(offendingClass, offendingAttribute.getName()); 743 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, fieldName); 744 return false; 745 } 746 int count = 0; 747 for (Iterator iter = ((Collection) value).iterator(); iter.hasNext();) { 748 errorKey.append(mappedNames[0] + "[" + count + "]."); 749 count++; 750 valid &= validateFieldRequirementByChartHelper(iter.next(), ArrayUtils.subarray(mappedNames, 1, mappedNames.length), errorKey, itemNumber); 751 } 752 return valid; 753 } 754 else if (StringUtils.isBlank(value.toString())) { 755 errorKey.append(mappedNames[0]); 756 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, (String) mappedNames[0]); 757 return false; 758 } 759 else if (mappedNames.length > 1) { 760 // this means we have not reached the end of a single field to be traversed yet, so continue with the recursion. 761 errorKey.append(mappedNames[0]).append("."); 762 valid &= validateFieldRequirementByChartHelper(value, ArrayUtils.subarray(mappedNames, 1, mappedNames.length), errorKey, itemNumber); 763 return valid; 764 } 765 else { 766 return true; 767 } 768 } 769 770 protected String getValueFromAvailabilityMatrix(String fieldName, String systemType, String systemState) { 771 for (AvailabilityMatrix am : PurapConstants.CAMS_AVAILABILITY_MATRIX.MATRIX_LIST) { 772 if (am.fieldName.equals(fieldName) && am.systemState.equals(systemState) && am.systemType.equals(systemType)) { 773 return am.availableValue; 774 } 775 } 776 // if we can't find any matching from availability matrix, return null for now. 777 return null; 778 } 779 780 /** 781 * Validates that the total quantity on all locations equals to the quantity on the line item. This is only used for IND system 782 * type. 783 * 784 * @param capitalAssetItems 785 * @return true if the total quantity on all locations equals to the quantity on the line item. 786 */ 787 protected boolean validateQuantityOnLocationsEqualsQuantityOnItem(List<PurchasingCapitalAssetItem> capitalAssetItems, String systemType, String systemState) { 788 boolean valid = true; 789 String availableValue = getValueFromAvailabilityMatrix(PurapPropertyConstants.CAPITAL_ASSET_LOCATIONS + "." + PurapPropertyConstants.QUANTITY, systemType, systemState); 790 if (availableValue.equals(PurapConstants.CapitalAssetAvailability.NONE)) { 791 // If the location quantity isn't available on the document given the system type and system state, we don't need to 792 // validate this, just return true. 793 return true; 794 } 795 int count = 0; 796 for (PurchasingCapitalAssetItem item : capitalAssetItems) { 797 if (item.getPurchasingItem() != null && item.getPurchasingItem().getItemType().isQuantityBasedGeneralLedgerIndicator() && !item.getPurchasingCapitalAssetSystem().getCapitalAssetLocations().isEmpty()) { 798 KualiDecimal total = new KualiDecimal(0); 799 for (CapitalAssetLocation location : item.getPurchasingCapitalAssetSystem().getCapitalAssetLocations()) { 800 if (ObjectUtils.isNotNull(location.getItemQuantity())) { 801 total = total.add(location.getItemQuantity()); 802 } 803 } 804 if (!item.getPurchasingItem().getItemQuantity().equals(total)) { 805 valid = false; 806 String errorKey = PurapKeyConstants.ERROR_CAPITAL_ASSET_LOCATIONS_QUANTITY_MUST_EQUAL_ITEM_QUANTITY; 807 String propertyName = "document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS + "[" + count + "]." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_SYSTEM + ".newPurchasingCapitalAssetLocationLine." + PurapPropertyConstants.QUANTITY; 808 GlobalVariables.getMessageMap().putError(propertyName, errorKey, Integer.toString(count + 1)); 809 } 810 } 811 count++; 812 } 813 814 return valid; 815 } 816 817 /** 818 * Validates for the individual system type that for each of the items, the capitalAssetTransactionTypeCode matches the system 819 * parameter PURCHASING_ASSET_TRANSACTION_TYPES_ALLOWING_ASSET_NUMBERS, the method will return true but if it doesn't match the 820 * system parameter, then loop through each of the itemCapitalAssets. If there is any non-null capitalAssetNumber then return 821 * false. 822 * 823 * @param capitalAssetItems the List of PurchasingCapitalAssetItems on the document to be validated 824 * @return false if the capital asset transaction type does not match the system parameter that allows asset numbers but the 825 * itemCapitalAsset contains at least one asset numbers. 826 */ 827 protected boolean validateIndividualSystemPurchasingTransactionTypesAllowingAssetNumbers(List<PurchasingCapitalAssetItem> capitalAssetItems) { 828 boolean valid = true; 829 int count = 0; 830 for (PurchasingCapitalAssetItem capitalAssetItem : capitalAssetItems) { 831 String prefix = "document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS + "[" + count + "]."; 832 valid &= validatePurchasingTransactionTypesAllowingAssetNumbers(capitalAssetItem.getPurchasingCapitalAssetSystem(), capitalAssetItem.getCapitalAssetTransactionTypeCode(), prefix); 833 count++; 834 } 835 return valid; 836 } 837 838 /** 839 * Generic validation that if the capitalAssetTransactionTypeCode does not match the system parameter 840 * PURCHASING_ASSET_TRANSACTION_TYPES_ALLOWING_ASSET_NUMBERS and at least one of the itemCapitalAssets contain a non-null 841 * capitalAssetNumber then return false. This method is used by one system and multiple system types as well as being used as a 842 * helper method for validateIndividualSystemPurchasingTransactionTypesAllowingAssetNumbers method. 843 * 844 * @param capitalAssetSystem the capitalAssetSystem containing a List of itemCapitalAssets to be validated. 845 * @param capitalAssetTransactionType the capitalAssetTransactionTypeCode containing asset numbers to be validated. 846 * @return false if the capital asset transaction type does not match the system parameter that allows asset numbers but the 847 * itemCapitalAsset contains at least one asset numbers. 848 */ 849 protected boolean validatePurchasingTransactionTypesAllowingAssetNumbers(CapitalAssetSystem capitalAssetSystem, String capitalAssetTransactionType, String prefix) { 850 boolean allowedAssetNumbers = (this.getParameterService().getParameterEvaluator(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, CabParameterConstants.CapitalAsset.PURCHASING_ASSET_TRANSACTION_TYPES_ALLOWING_ASSET_NUMBERS, capitalAssetTransactionType).evaluationSucceeds()); 851 if (allowedAssetNumbers) { 852 // If this is a transaction type that allows asset numbers, we don't need to validate anymore, just return true here. 853 return true; 854 } 855 else { 856 for (ItemCapitalAsset asset : capitalAssetSystem.getItemCapitalAssets()) { 857 if (asset.getCapitalAssetNumber() != null) { 858 String propertyName = prefix + PurapPropertyConstants.CAPITAL_ASSET_TRANSACTION_TYPE_CODE; 859 GlobalVariables.getMessageMap().putError(propertyName, PurapKeyConstants.ERROR_CAPITAL_ASSET_ASSET_NUMBERS_NOT_ALLOWED_TRANS_TYPE, capitalAssetTransactionType); 860 return false; 861 } 862 } 863 } 864 return true; 865 } 866 867 /** 868 * TODO: rename this method removing trade in reference? Validates that if the non quantity drive allowed indicator on the 869 * capital asset transaction type is false and the item is of non quantity type 870 * 871 * @param capitalAssetItems The List of PurchasingCapitalAssetItem to be validated. 872 * @return false if the indicator is false and there is at least one non quantity items on the list. 873 */ 874 protected boolean validateNonQuantityDrivenAllowedIndicatorAndTradeIn(List<PurchasingCapitalAssetItem> capitalAssetItems) { 875 boolean valid = true; 876 int count = 0; 877 for (PurchasingCapitalAssetItem capitalAssetItem : capitalAssetItems) { 878 String prefix = "document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS + "[" + count + "]."; 879 if (StringUtils.isNotBlank(capitalAssetItem.getCapitalAssetTransactionTypeCode())) { 880 // ((PurchasingCapitalAssetItemBase) 881 // capitalAssetItem).refreshReferenceObject(PurapPropertyConstants.CAPITAL_ASSET_TRANSACTION_TYPE); 882 if (!capitalAssetItem.getCapitalAssetTransactionType().getCapitalAssetNonquantityDrivenAllowIndicator()) { 883 if (!capitalAssetItem.getPurchasingItem().getItemType().isQuantityBasedGeneralLedgerIndicator()) { 884 String propertyName = prefix + PurapPropertyConstants.CAPITAL_ASSET_TRANSACTION_TYPE_CODE; 885 GlobalVariables.getMessageMap().putError(propertyName, PurapKeyConstants.ERROR_CAPITAL_ASSET_TRANS_TYPE_NOT_ALLOWING_NON_QUANTITY_ITEMS, capitalAssetItem.getCapitalAssetTransactionTypeCode()); 886 valid &= false; 887 } 888 } 889 } 890 count++; 891 } 892 return valid; 893 } 894 895 /** 896 * Wrapper to do Capital Asset validations, generating errors instead of warnings. Makes sure that the given item's data 897 * relevant to its later possible classification as a Capital Asset is internally consistent, by marshaling and calling the 898 * methods marked as Capital Asset validations. This implementation assumes that all object codes are valid (real) object codes. 899 * 900 * @param recurringPaymentType The item's document's RecurringPaymentType 901 * @param item A PurchasingItemBase object 902 * @param apoCheck True if this check is for APO purposes 903 * @return True if the item passes all Capital Asset validations 904 */ 905 public boolean validateItemCapitalAssetWithErrors(String recurringPaymentTypeCode, ExternalPurApItem item, boolean apoCheck) { 906 PurchasingItemBase purchasingItem = (PurchasingItemBase) item; 907 List<String> previousErrorPath = GlobalVariables.getMessageMap().getErrorPath(); 908 GlobalVariables.getMessageMap().clearErrorPath(); 909 GlobalVariables.getMessageMap().addToErrorPath(PurapConstants.CAPITAL_ASSET_TAB_ERRORS); 910 boolean result = validatePurchasingItemCapitalAsset(recurringPaymentTypeCode, purchasingItem); 911 GlobalVariables.getMessageMap().clearErrorPath(); 912 return result; 913 } 914 915 /** 916 * Makes sure that the given item's data relevant to its later possible classification as a Capital Asset is internally 917 * consistent, by marshaling and calling the methods marked as Capital Asset validations. This implementation assumes that all 918 * object codes are valid (real) object codes. 919 * 920 * @param recurringPaymentType The item's document's RecurringPaymentType 921 * @param item A PurchasingItemBase object 922 * @param warn A boolean which should be set to true if warnings are to be set on the calling document for most of the 923 * validations, rather than errors. 924 * @return True if the item passes all Capital Asset validations 925 */ 926 protected boolean validatePurchasingItemCapitalAsset(String recurringPaymentTypeCode, PurchasingItem item) { 927 boolean valid = true; 928 String capitalAssetTransactionTypeCode = ""; 929 AssetTransactionType capitalAssetTransactionType = null; 930 String itemIdentifier = item.getItemIdentifierString(); 931 932 if (item.getPurchasingCapitalAssetItem() != null) { 933 capitalAssetTransactionTypeCode = item.getPurchasingCapitalAssetItem().getCapitalAssetTransactionTypeCode(); 934 // ((PurchasingCapitalAssetItemBase) 935 // item.getPurchasingCapitalAssetItem()).refreshReferenceObject(PurapPropertyConstants.CAPITAL_ASSET_TRANSACTION_TYPE); 936 capitalAssetTransactionType = (AssetTransactionType) item.getPurchasingCapitalAssetItem().getCapitalAssetTransactionType(); 937 } 938 939 // These checks do not depend on Accounting Line information, but do depend on transaction type. 940 if (!StringUtils.isEmpty(capitalAssetTransactionTypeCode)) { 941 valid &= validateCapitalAssetTransactionTypeVersusRecurrence(capitalAssetTransactionType, recurringPaymentTypeCode, itemIdentifier); 942 } 943 return valid &= validatePurapItemCapitalAsset(item, capitalAssetTransactionType); 944 } 945 946 /** 947 * This method validated purapItem giving a transtype 948 * 949 * @param recurringPaymentType 950 * @param item 951 * @param warn 952 * @return 953 */ 954 protected boolean validatePurapItemCapitalAsset(PurApItem item, AssetTransactionType capitalAssetTransactionType) { 955 boolean valid = true; 956 String itemIdentifier = item.getItemIdentifierString(); 957 boolean quantityBased = item.getItemType().isQuantityBasedGeneralLedgerIndicator(); 958 BigDecimal itemUnitPrice = item.getItemUnitPrice(); 959 HashSet<String> capitalOrExpenseSet = new HashSet<String>(); // For the first validation on every accounting line. 960 961 962 // Do the checks that depend on Accounting Line information. 963 for (PurApAccountingLine accountingLine : item.getSourceAccountingLines()) { 964 // Because of ObjectCodeCurrent, we had to refresh this. 965 accountingLine.refreshReferenceObject(KFSPropertyConstants.OBJECT_CODE); 966 ObjectCode objectCode = accountingLine.getObjectCode(); 967 968 if (ObjectUtils.isNotNull(objectCode)) { 969 String capitalOrExpense = objectCodeCapitalOrExpense(objectCode); 970 capitalOrExpenseSet.add(capitalOrExpense); // HashSets accumulate distinct values (and nulls) only. 971 972 valid &= validateAccountingLinesNotCapitalAndExpense(capitalOrExpenseSet, itemIdentifier, objectCode); 973 974 975 // Do the checks involving capital asset transaction type. 976 if (capitalAssetTransactionType != null) { 977 valid &= validateObjectCodeVersusTransactionType(objectCode, capitalAssetTransactionType, itemIdentifier, quantityBased); 978 } 979 } 980 } 981 return valid; 982 } 983 984 /** 985 * Capital Asset validation: An item cannot have among its associated accounting lines both object codes that indicate it is a 986 * Capital Asset, and object codes that indicate that the item is not a Capital Asset. Whether an object code indicates that the 987 * item is a Capital Asset is determined by whether its level is among a specific set of levels that are deemed acceptable for 988 * such items. 989 * 990 * @param capitalOrExpenseSet A HashSet containing the distinct values of either "Capital" or "Expense" that have been added to 991 * it. 992 * @param warn A boolean which should be set to true if warnings are to be set on the calling document 993 * @param itemIdentifier A String identifying the item for error display 994 * @param objectCode An ObjectCode, for error display 995 * @return True if the given HashSet contains at most one of either "Capital" or "Expense" 996 */ 997 protected boolean validateAccountingLinesNotCapitalAndExpense(HashSet<String> capitalOrExpenseSet, String itemIdentifier, ObjectCode objectCode) { 998 boolean valid = true; 999 // If the set contains more than one distinct string, fail. 1000 if (capitalOrExpenseSet.size() > 1) { 1001 GlobalVariables.getMessageMap().putError(KFSConstants.FINANCIAL_OBJECT_LEVEL_CODE_PROPERTY_NAME, CabKeyConstants.ERROR_ITEM_CAPITAL_AND_EXPENSE, itemIdentifier, objectCode.getFinancialObjectCodeName()); 1002 valid &= false; 1003 } 1004 return valid; 1005 } 1006 1007 protected boolean validateLevelCapitalAssetIndication(BigDecimal unitPrice, ObjectCode objectCode, String itemIdentifier) { 1008 1009 String capitalAssetPriceThresholdParam = this.getParameterService().getParameterValue(AssetGlobal.class, CabParameterConstants.CapitalAsset.CAPITALIZATION_LIMIT_AMOUNT); 1010 BigDecimal priceThreshold = null; 1011 try { 1012 priceThreshold = new BigDecimal(capitalAssetPriceThresholdParam); 1013 } 1014 catch (NumberFormatException nfe) { 1015 throw new RuntimeException("the parameter for CAPITAL_ASSET_OBJECT_LEVELS came was not able to be converted to a number.", nfe); 1016 } 1017 if (unitPrice.compareTo(priceThreshold) >= 0) { 1018 List<String> possibleCAMSObjectLevels = this.getParameterService().getParameterValues(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, CabParameterConstants.CapitalAsset.POSSIBLE_CAPITAL_ASSET_OBJECT_LEVELS); 1019 if (possibleCAMSObjectLevels.contains(objectCode.getFinancialObjectLevelCode())) { 1020 1021 String warning = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CabKeyConstants.WARNING_ABOVE_THRESHOLD_SUGESTS_CAPITAL_ASSET_LEVEL); 1022 warning = StringUtils.replace(warning, "{0}", itemIdentifier); 1023 warning = StringUtils.replace(warning, "{1}", priceThreshold.toString()); 1024 GlobalVariables.getMessageList().add(warning); 1025 1026 return false; 1027 } 1028 } 1029 return true; 1030 } 1031 1032 /** 1033 * Capital Asset validation: If the item has a transaction type, check that the transaction type is acceptable for the object 1034 * code sub-types of all the object codes on the associated accounting lines. 1035 * 1036 * @param objectCode 1037 * @param capitalAssetTransactionType 1038 * @param warn A boolean which should be set to true if warnings are to be set on the calling document 1039 * @param itemIdentifier 1040 * @return 1041 */ 1042 protected boolean validateObjectCodeVersusTransactionType(ObjectCode objectCode, CapitalAssetBuilderAssetTransactionType capitalAssetTransactionType, String itemIdentifier, boolean quantityBasedItem) { 1043 boolean valid = true; 1044 String[] objectCodeSubTypes = {}; 1045 1046 1047 if (quantityBasedItem) { 1048 String capitalAssetQuantitySubtypeRequiredText = capitalAssetTransactionType.getCapitalAssetQuantitySubtypeRequiredText(); 1049 if (capitalAssetQuantitySubtypeRequiredText != null) { 1050 objectCodeSubTypes = StringUtils.split(capitalAssetQuantitySubtypeRequiredText, ";"); 1051 } 1052 } 1053 else { 1054 String capitalAssetNonquantitySubtypeRequiredText = capitalAssetTransactionType.getCapitalAssetNonquantitySubtypeRequiredText(); 1055 if (capitalAssetNonquantitySubtypeRequiredText != null) { 1056 objectCodeSubTypes = StringUtils.split(capitalAssetNonquantitySubtypeRequiredText, ";"); 1057 } 1058 } 1059 1060 boolean found = false; 1061 for (String subType : objectCodeSubTypes) { 1062 if (StringUtils.equals(subType, objectCode.getFinancialObjectSubTypeCode())) { 1063 found = true; 1064 break; 1065 } 1066 } 1067 1068 if (!found) { 1069 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_CAPITAL_ASSET_TRANSACTION_TYPE, CabKeyConstants.ERROR_ITEM_TRAN_TYPE_OBJECT_CODE_SUBTYPE, itemIdentifier, capitalAssetTransactionType.getCapitalAssetTransactionTypeDescription(), objectCode.getFinancialObjectCodeName(), objectCode.getFinancialObjectSubType().getFinancialObjectSubTypeName()); 1070 1071 valid &= false; 1072 } 1073 return valid; 1074 } 1075 1076 /** 1077 * Capital Asset validation: If the item has a transaction type, check that if the document specifies that recurring payments 1078 * are to be made, that the transaction type is one that is appropriate for this situation, and that if the document does not 1079 * specify that recurring payments are to be made, that the transaction type is one that is appropriate for that situation. 1080 * 1081 * @param capitalAssetTransactionType 1082 * @param recurringPaymentType 1083 * @param warn A boolean which should be set to true if warnings are to be set on the calling document 1084 * @param itemIdentifier 1085 * @return 1086 */ 1087 protected boolean validateCapitalAssetTransactionTypeVersusRecurrence(CapitalAssetBuilderAssetTransactionType capitalAssetTransactionType, String recurringPaymentTypeCode, String itemIdentifier) { 1088 boolean valid = true; 1089 1090 // If there is a tran type ... 1091 if ((capitalAssetTransactionType != null) && (capitalAssetTransactionType.getCapitalAssetTransactionTypeCode() != null)) { 1092 String recurringTransactionTypeCodes = this.getParameterService().getParameterValue(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, CabParameterConstants.CapitalAsset.RECURRING_CAMS_TRAN_TYPES); 1093 1094 1095 if (StringUtils.isNotEmpty(recurringPaymentTypeCode)) { // If there is a recurring payment type ... 1096 if (!StringUtils.contains(recurringTransactionTypeCodes, capitalAssetTransactionType.getCapitalAssetTransactionTypeCode())) { 1097 // There should be a recurring tran type code. 1098 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_CAPITAL_ASSET_TRANSACTION_TYPE, CabKeyConstants.ERROR_ITEM_WRONG_TRAN_TYPE, itemIdentifier, capitalAssetTransactionType.getCapitalAssetTransactionTypeDescription(), CabConstants.ValidationStrings.RECURRING); 1099 valid &= false; 1100 } 1101 } 1102 else { // If the payment type is not recurring ... 1103 // There should not be a recurring transaction type code. 1104 if (StringUtils.contains(recurringTransactionTypeCodes, capitalAssetTransactionType.getCapitalAssetTransactionTypeCode())) { 1105 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_CAPITAL_ASSET_TRANSACTION_TYPE, CabKeyConstants.ERROR_ITEM_WRONG_TRAN_TYPE, itemIdentifier, capitalAssetTransactionType.getCapitalAssetTransactionTypeDescription(), CabConstants.ValidationStrings.NON_RECURRING); 1106 1107 valid &= false; 1108 } 1109 } 1110 } 1111 else { // If there is no transaction type ... 1112 if (StringUtils.isNotEmpty(recurringPaymentTypeCode)) { // If there is a recurring payment type ... 1113 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_CAPITAL_ASSET_TRANSACTION_TYPE, CabKeyConstants.ERROR_ITEM_NO_TRAN_TYPE, itemIdentifier, CabConstants.ValidationStrings.RECURRING); 1114 valid &= false; 1115 } 1116 else { // If the payment type is not recurring ... 1117 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_CAPITAL_ASSET_TRANSACTION_TYPE, CabKeyConstants.ERROR_ITEM_NO_TRAN_TYPE, itemIdentifier, CabConstants.ValidationStrings.NON_RECURRING); 1118 valid &= false; 1119 } 1120 } 1121 1122 return valid; 1123 } 1124 1125 /** 1126 * Utility wrapping isCapitalAssetObjectCode for the use of processItemCapitalAssetValidation. 1127 * 1128 * @param oc An ObjectCode 1129 * @return A String indicating that the given object code is either Capital or Expense 1130 */ 1131 protected String objectCodeCapitalOrExpense(ObjectCode oc) { 1132 String capital = CabConstants.ValidationStrings.CAPITAL; 1133 String expense = CabConstants.ValidationStrings.EXPENSE; 1134 return (isCapitalAssetObjectCode(oc) ? capital : expense); 1135 } 1136 1137 /** 1138 * Predicate to determine whether the given object code is of a specified object sub type required for purap cams. 1139 * 1140 * @param oc An ObjectCode 1141 * @return True if the ObjectSubType is the one designated for capital assets. 1142 */ 1143 protected boolean isCapitalAssetObjectCode(ObjectCode oc) { 1144 String capitalAssetObjectSubType = this.getParameterService().getParameterValue(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, PurapParameterConstants.CapitalAsset.PURCHASING_OBJECT_SUB_TYPES); 1145 return (StringUtils.containsIgnoreCase(capitalAssetObjectSubType, oc.getFinancialObjectSubTypeCode()) ? true : false); 1146 } 1147 1148 /** 1149 * Not documented 1150 * @param capitalAssetSystems 1151 * @return 1152 */ 1153 protected boolean validateCapitalAssetLocationAddressFieldsOneOrMultipleSystemType(List<CapitalAssetSystem> capitalAssetSystems) { 1154 boolean valid = true; 1155 boolean ignoreRoom = false; 1156 int systemCount = 0; 1157 for (CapitalAssetSystem system : capitalAssetSystems) { 1158 List<CapitalAssetLocation> capitalAssetLocations = system.getCapitalAssetLocations(); 1159 StringBuffer errorKey = new StringBuffer("document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_SYSTEMS + "[" + new Integer(systemCount++) + "]."); 1160 errorKey.append("capitalAssetLocations"); 1161 int locationCount = 0; 1162 for (CapitalAssetLocation location : capitalAssetLocations) { 1163 errorKey.append("[" + locationCount++ + "]."); 1164 AssetType assetType = getAssetType(system.getCapitalAssetTypeCode()); 1165 ignoreRoom = ObjectUtils.isNull(assetType) ? false : assetType.isMovingIndicator(); 1166 valid &= validateCapitalAssetLocationAddressFields(location, ignoreRoom, errorKey); 1167 } 1168 } 1169 return valid; 1170 } 1171 1172 /** 1173 * Not Documented 1174 * @param capitalAssetItems 1175 * @return 1176 */ 1177 protected boolean validateCapitalAssetLocationAddressFieldsForIndividualSystemType(List<PurchasingCapitalAssetItem> capitalAssetItems) { 1178 boolean valid = true; 1179 boolean ignoreRoom = false; 1180 for (PurchasingCapitalAssetItem item : capitalAssetItems) { 1181 CapitalAssetSystem system = item.getPurchasingCapitalAssetSystem(); 1182 List<CapitalAssetLocation> capitalAssetLocations = system.getCapitalAssetLocations(); 1183 StringBuffer errorKey = new StringBuffer("document." + PurapPropertyConstants.PURCHASING_CAPITAL_ASSET_ITEMS + "[" + new Integer(item.getPurchasingItem().getItemLineNumber().intValue() - 1) + "]."); 1184 errorKey.append("purchasingCapitalAssetSystem.capitalAssetLocations"); 1185 int i = 0; 1186 for (CapitalAssetLocation location : capitalAssetLocations) { 1187 errorKey.append("[" + i++ + "]."); 1188 AssetType assetType = getAssetType(system.getCapitalAssetTypeCode()); 1189 ignoreRoom = ObjectUtils.isNull(assetType) ? false : assetType.isMovingIndicator(); 1190 valid &= validateCapitalAssetLocationAddressFields(location, ignoreRoom, errorKey); 1191 } 1192 } 1193 return valid; 1194 } 1195 1196 /** 1197 * Not documented 1198 * @param location 1199 * @param ignoreRoom Is used to identify if room number should be validated. If true then room is ignored in this validation. 1200 * @param errorKey 1201 * @return 1202 */ 1203 protected boolean validateCapitalAssetLocationAddressFields(CapitalAssetLocation location, boolean ignoreRoom, StringBuffer errorKey) { 1204 boolean valid = true; 1205 if (StringUtils.isBlank(location.getCapitalAssetLine1Address())) { 1206 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_ADDRESS_LINE1); 1207 valid &= false; 1208 } 1209 if (StringUtils.isBlank(location.getCapitalAssetCityName())) { 1210 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_CITY); 1211 valid &= false; 1212 } 1213 if (StringUtils.isBlank(location.getCapitalAssetCountryCode())) { 1214 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_COUNTRY); 1215 valid &= false; 1216 } 1217 else if (location.getCapitalAssetCountryCode().equals(KFSConstants.COUNTRY_CODE_UNITED_STATES)) { 1218 if (StringUtils.isBlank(location.getCapitalAssetStateCode())) { 1219 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_STATE); 1220 valid &= false; 1221 } 1222 if (StringUtils.isBlank(location.getCapitalAssetPostalCode())) { 1223 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_POSTAL_CODE); 1224 valid &= false; 1225 } 1226 } 1227 if (!location.isOffCampusIndicator()) { 1228 if (StringUtils.isBlank(location.getCampusCode())) { 1229 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_CAMPUS); 1230 valid &= false; 1231 } 1232 if (StringUtils.isBlank(location.getBuildingCode())) { 1233 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_BUILDING); 1234 valid &= false; 1235 } 1236 1237 if (StringUtils.isBlank(location.getBuildingRoomNumber()) && !ignoreRoom) { 1238 GlobalVariables.getMessageMap().putError(errorKey.toString(), KFSKeyConstants.ERROR_REQUIRED, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_ROOM); 1239 valid &= false; 1240 } 1241 1242 if (!StringUtils.isBlank(location.getBuildingRoomNumber()) && ignoreRoom) { 1243 GlobalVariables.getMessageMap().putError(errorKey.toString(), CamsKeyConstants.AssetLocation.ERROR_ASSET_LOCATION_ROOM_NUMBER_NONMOVEABLE, PurapPropertyConstants.CAPITAL_ASSET_LOCATION_ROOM); 1244 valid &= false; 1245 } 1246 } 1247 return valid; 1248 } 1249 1250 protected boolean validateAccountsPayableItem(AccountsPayableItem apItem) { 1251 boolean valid = true; 1252 valid &= validatePurapItemCapitalAsset(apItem, (AssetTransactionType) apItem.getCapitalAssetTransactionType()); 1253 return valid; 1254 } 1255 1256 // end of methods for purap 1257 1258 /** 1259 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#validateFinancialProcessingData(org.kuali.kfs.sys.document.AccountingDocument, 1260 * org.kuali.kfs.fp.businessobject.CapitalAssetInformation) 1261 * @param accountingDocument and capitalAssetInformation 1262 * @return True if the FinancialProcessingData is valid. 1263 */ 1264 public boolean validateFinancialProcessingData(AccountingDocument accountingDocument, CapitalAssetInformation capitalAssetInformation) { 1265 boolean valid = true; 1266 1267 // Check if we need to collect cams data 1268 AccountCapitalObjectCode accountCapitalObjectCode = this.getCapitalAssetObjectSubTypeLinesFlag(accountingDocument); 1269 boolean isNewAssetBlank = isNewAssetBlank(capitalAssetInformation); 1270 boolean isUpdateAssetBlank = isUpdateAssetBlank(capitalAssetInformation); 1271 1272 // validate asset information provided by the user are allowed 1273 valid = accountCapitalObjectCode.validateAssetInfoAllowed(accountingDocument, isNewAssetBlank, isUpdateAssetBlank); 1274 1275 if (valid) { 1276 // non-capital object code, no further check needed for asset 1277 if (AccountCapitalObjectCode.BOTH_NONCAP.equals(accountCapitalObjectCode)) { 1278 return true; 1279 } 1280 else { 1281 if (!isUpdateAssetBlank) { 1282 // Validate update Asset information 1283 valid &= validateUpdateCapitalAssetField(capitalAssetInformation, accountingDocument); 1284 } 1285 else { 1286 // Validate New Asset information 1287 valid &= checkNewCapitalAssetFieldsExist(capitalAssetInformation, accountingDocument); 1288 if (valid) { 1289 valid = validateNewCapitalAssetFields(capitalAssetInformation); 1290 } 1291 } 1292 } 1293 } 1294 return valid; 1295 } 1296 1297 /** 1298 * Get the Capital Asset Object Code from the accounting lines. 1299 * 1300 * @param accountingDocument 1301 * @return getCapitalAssetObjectSubTypeLinesFlag = AccountCapitalObjectCode.NON_CAPITAL ==> no assetObjectSubType code 1302 * AccountCapitalObjectCode.FROM_CAPITAL ==> assetObjectSubType code on source lines 1303 * AccountCapitalObjectCode.FROM_CAPITAL ==> assetObjectSubType code on target lines 1304 * AccountCapitalObjectCode.BOTH_CAPITAL ==> assetObjectSubType code on both source and target lines 1305 */ 1306 protected AccountCapitalObjectCode getCapitalAssetObjectSubTypeLinesFlag(AccountingDocument accountingDocument) { 1307 List<String> financialProcessingCapitalObjectSubTypes = this.getParameterService().getParameterValues(KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, CabParameterConstants.CapitalAsset.FINANCIAL_PROCESSING_CAPITAL_OBJECT_SUB_TYPES); 1308 AccountCapitalObjectCode capitalAssetObjectSubTypeLinesFlag = AccountCapitalObjectCode.BOTH_NONCAP; 1309 1310 // Check if SourceAccountingLine has objectSub type code 1311 List<SourceAccountingLine> sAccountingLines = accountingDocument.getSourceAccountingLines(); 1312 for (SourceAccountingLine sourceAccountingLine : sAccountingLines) { 1313 ObjectCode objectCode = sourceAccountingLine.getObjectCode(); 1314 1315 if (ObjectUtils.isNotNull(objectCode)) { 1316 String objectSubTypeCode = objectCode.getFinancialObjectSubTypeCode(); 1317 if (financialProcessingCapitalObjectSubTypes.contains(objectSubTypeCode)) { 1318 capitalAssetObjectSubTypeLinesFlag = AccountCapitalObjectCode.FROM_CAPITAL_TO_NONCAP; 1319 break; 1320 } 1321 } 1322 } 1323 1324 // Check if TargetAccountingLine has objectSub type code 1325 List<TargetAccountingLine> tAccountingLines = accountingDocument.getTargetAccountingLines(); 1326 for (TargetAccountingLine targetAccountingLine : tAccountingLines) { 1327 ObjectCode objectCode = targetAccountingLine.getObjectCode(); 1328 1329 if (ObjectUtils.isNotNull(objectCode)) { 1330 String objectSubTypeCode = objectCode.getFinancialObjectSubTypeCode(); 1331 if (financialProcessingCapitalObjectSubTypes.contains(objectSubTypeCode)) { 1332 capitalAssetObjectSubTypeLinesFlag = capitalAssetObjectSubTypeLinesFlag == AccountCapitalObjectCode.FROM_CAPITAL_TO_NONCAP ? AccountCapitalObjectCode.BOTH_CAPITAL : AccountCapitalObjectCode.FROM_NONCAP_TO_CAPITAL; 1333 break; 1334 } 1335 } 1336 } 1337 1338 return capitalAssetObjectSubTypeLinesFlag; 1339 } 1340 1341 /** 1342 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#hasCapitalAssetObjectSubType(org.kuali.kfs.sys.document.AccountingDocument) 1343 */ 1344 public boolean hasCapitalAssetObjectSubType(AccountingDocument accountingDocument) { 1345 final AccountCapitalObjectCode accountCapitalObjectCode = getCapitalAssetObjectSubTypeLinesFlag(accountingDocument); 1346 return accountCapitalObjectCode != null && !accountCapitalObjectCode.equals(AccountCapitalObjectCode.BOTH_NONCAP); 1347 } 1348 1349 1350 /** 1351 * To check if data exists on create new asset 1352 * 1353 * @param capitalAssetInformation 1354 * @return boolean false if the new asset is not blank 1355 */ 1356 protected boolean isNewAssetBlank(CapitalAssetInformation capitalAssetInformation) { 1357 boolean isBlank = true; 1358 1359 if (ObjectUtils.isNotNull(capitalAssetInformation.getCapitalAssetTypeCode()) || ObjectUtils.isNotNull(capitalAssetInformation.getVendorName()) || ObjectUtils.isNotNull(capitalAssetInformation.getCapitalAssetQuantity()) || ObjectUtils.isNotNull(capitalAssetInformation.getCapitalAssetManufacturerName()) || ObjectUtils.isNotNull(capitalAssetInformation.getCapitalAssetManufacturerModelNumber()) || ObjectUtils.isNotNull(capitalAssetInformation.getCapitalAssetDescription())) { 1360 isBlank = false; 1361 } 1362 return isBlank; 1363 } 1364 1365 /** 1366 * To check if data exists on update asset 1367 * 1368 * @param capitalAssetInformation 1369 * @return boolean false if the update asset is not blank 1370 */ 1371 protected boolean isUpdateAssetBlank(CapitalAssetInformation capitalAssetInformation) { 1372 boolean isBlank = true; 1373 1374 if (ObjectUtils.isNotNull(capitalAssetInformation.getCapitalAssetNumber())) { 1375 isBlank = false; 1376 } 1377 return isBlank; 1378 } 1379 1380 /** 1381 * To validate if the asset is active 1382 * 1383 * @param capitalAssetManagementAsset the asset to be validated 1384 * @return boolean false if the asset is not active 1385 */ 1386 protected boolean validateUpdateCapitalAssetField(CapitalAssetInformation capitalAssetInformation, AccountingDocument accountingDocument) { 1387 boolean valid = true; 1388 1389 Map<String, String> params = new HashMap<String, String>(); 1390 params.put(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, capitalAssetInformation.getCapitalAssetNumber().toString()); 1391 Asset asset = (Asset) this.getBusinessObjectService().findByPrimaryKey(Asset.class, params); 1392 1393 List<Long> assetNumbers = new ArrayList<Long>(); 1394 assetNumbers.add(capitalAssetInformation.getCapitalAssetNumber()); 1395 1396 if (ObjectUtils.isNull(asset)) { 1397 valid = false; 1398 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, KFSPropertyConstants.CAPITAL_ASSET_NUMBER); 1399 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, KFSKeyConstants.ERROR_EXISTENCE, label); 1400 } 1401 else if (!(this.getAssetService().isCapitalAsset(asset) && !this.getAssetService().isAssetRetired(asset))) { 1402 // check asset status must be capital asset active. 1403 valid = false; 1404 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_NUMBER, CabKeyConstants.CapitalAssetInformation.ERROR_ASSET_ACTIVE_CAPITAL_ASSET_REQUIRED); 1405 } 1406 else { 1407 String documentNumber = accountingDocument.getDocumentNumber(); 1408 String documentType = getDocumentTypeName(accountingDocument); 1409 1410 if (getCapitalAssetManagementModuleService().isFpDocumentEligibleForAssetLock(accountingDocument, documentType) && getCapitalAssetManagementModuleService().isAssetLocked(assetNumbers, documentType, documentNumber)) { 1411 valid = false; 1412 } 1413 } 1414 return valid; 1415 } 1416 1417 /** 1418 * Check if all required fields exist on new asset 1419 * 1420 * @param capitalAssetInformation the fields of add asset to be checked 1421 * @return boolean false if a required field is missing 1422 */ 1423 protected boolean checkNewCapitalAssetFieldsExist(CapitalAssetInformation capitalAssetInformation, AccountingDocument accountingDocument) { 1424 boolean valid = true; 1425 1426 if (StringUtils.isBlank(capitalAssetInformation.getCapitalAssetTypeCode())) { 1427 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, KFSPropertyConstants.CAPITAL_ASSET_TYPE_CODE); 1428 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_TYPE_CODE, KFSKeyConstants.ERROR_REQUIRED, label); 1429 valid = false; 1430 } 1431 1432 if (capitalAssetInformation.getCapitalAssetQuantity() == null || capitalAssetInformation.getCapitalAssetQuantity() <= 0) { 1433 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, KFSPropertyConstants.CAPITAL_ASSET_QUANTITY); 1434 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_QUANTITY, KFSKeyConstants.ERROR_REQUIRED, label); 1435 valid = false; 1436 } 1437 1438 // skip vendor name required validation for procurement card document 1439 if (!(accountingDocument instanceof ProcurementCardDocument) && StringUtils.isBlank(capitalAssetInformation.getVendorName())) { 1440 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, KFSPropertyConstants.VENDOR_NAME); 1441 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.VENDOR_NAME, KFSKeyConstants.ERROR_REQUIRED, label); 1442 valid = false; 1443 } 1444 1445 if (StringUtils.isBlank(capitalAssetInformation.getCapitalAssetManufacturerName())) { 1446 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, KFSPropertyConstants.CAPITAL_ASSET_MANUFACTURE_NAME); 1447 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_MANUFACTURE_NAME, KFSKeyConstants.ERROR_REQUIRED, label); 1448 valid = false; 1449 } 1450 1451 if (StringUtils.isBlank(capitalAssetInformation.getCapitalAssetDescription())) { 1452 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, CamsPropertyConstants.Asset.CAPITAL_ASSET_DESCRIPTION); 1453 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.Asset.CAPITAL_ASSET_DESCRIPTION, KFSKeyConstants.ERROR_REQUIRED, label); 1454 valid = false; 1455 } 1456 1457 int index = 0; 1458 List<CapitalAssetInformationDetail> capitalAssetInformationDetails = capitalAssetInformation.getCapitalAssetInformationDetails(); 1459 for (CapitalAssetInformationDetail dtl : capitalAssetInformationDetails) { 1460 String errorPathPrefix = KFSPropertyConstants.DOCUMENT + "." + KFSPropertyConstants.CAPITAL_ASSET_INFORMATION + "." + KFSPropertyConstants.CAPITAL_ASSET_INFORMATION_DETAILS; 1461 1462 if (StringUtils.isBlank(dtl.getCampusCode())) { 1463 String label = this.getDataDictionaryService().getAttributeLabel(Campus.class, KFSPropertyConstants.CAMPUS_CODE); 1464 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.CAMPUS_CODE, KFSKeyConstants.ERROR_REQUIRED, label); 1465 valid = false; 1466 } 1467 1468 if (StringUtils.isBlank(dtl.getBuildingCode())) { 1469 String label = this.getDataDictionaryService().getAttributeLabel(Building.class, KFSPropertyConstants.BUILDING_CODE); 1470 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.BUILDING_CODE, KFSKeyConstants.ERROR_REQUIRED, label); 1471 valid = false; 1472 } 1473 1474 // Room is not required for non-moveable 1475 AssetType assetType = getAssetType(capitalAssetInformation.getCapitalAssetTypeCode()); 1476 if (ObjectUtils.isNull(assetType) || assetType.isMovingIndicator()) { 1477 if (StringUtils.isBlank(dtl.getBuildingRoomNumber())) { 1478 String label = this.getDataDictionaryService().getAttributeLabel(Room.class, KFSPropertyConstants.BUILDING_ROOM_NUMBER); 1479 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.BUILDING_ROOM_NUMBER, KFSKeyConstants.ERROR_REQUIRED, label); 1480 valid = false; 1481 } 1482 } 1483 1484 // Room number not allowed for non-moveable assets 1485 if (ObjectUtils.isNotNull(assetType) && !assetType.isMovingIndicator()) { 1486 if (StringUtils.isNotBlank(dtl.getBuildingRoomNumber())) { 1487 String label = this.getDataDictionaryService().getAttributeLabel(Room.class, KFSPropertyConstants.BUILDING_ROOM_NUMBER); 1488 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.BUILDING_ROOM_NUMBER, CamsKeyConstants.AssetLocation.ERROR_ASSET_LOCATION_ROOM_NUMBER_NONMOVEABLE, label); 1489 valid = false; 1490 } 1491 } 1492 1493 1494 1495 index++; 1496 } 1497 1498 return valid; 1499 } 1500 1501 /** 1502 * To validate new asset information 1503 * 1504 * @param capitalAssetInformation the information of add asset to be validated 1505 * @return boolean false if data is incorrect 1506 */ 1507 protected boolean validateNewCapitalAssetFields(CapitalAssetInformation capitalAssetInformation) { 1508 boolean valid = true; 1509 1510 if (!isAssetTypeExisting(capitalAssetInformation.getCapitalAssetTypeCode().toString())) { 1511 valid = false; 1512 String label = this.getDataDictionaryService().getAttributeLabel(CapitalAssetInformation.class, KFSPropertyConstants.CAPITAL_ASSET_TYPE_CODE); 1513 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_TYPE_CODE, KFSKeyConstants.ERROR_EXISTENCE, label); 1514 } 1515 1516 valid &= validateTotalNumberOfAssetTagLines(capitalAssetInformation); 1517 1518 int index = 0; 1519 List<CapitalAssetInformationDetail> capitalAssetInformationDetails = capitalAssetInformation.getCapitalAssetInformationDetails(); 1520 for (CapitalAssetInformationDetail dtl : capitalAssetInformationDetails) { 1521 // We have to explicitly call this DD service to uppercase each field. This may not be the best place and maybe form 1522 // populate is a better place but we CAMS team don't own FP document. This is the best we can do for now. 1523 SpringContext.getBean(BusinessObjectDictionaryService.class).performForceUppercase(dtl); 1524 String errorPathPrefix = KFSPropertyConstants.DOCUMENT + "." + KFSPropertyConstants.CAPITAL_ASSET_INFORMATION + "." + KFSPropertyConstants.CAPITAL_ASSET_INFORMATION_DETAILS; 1525 1526 if (StringUtils.isNotBlank(dtl.getCapitalAssetTagNumber()) && !dtl.getCapitalAssetTagNumber().equalsIgnoreCase(CamsConstants.Asset.NON_TAGGABLE_ASSET)) { 1527 if (isTagNumberDuplicate(capitalAssetInformationDetails, dtl)) { 1528 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.CAPITAL_ASSET_TAG_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_CAMPUS_TAG_NUMBER_DUPLICATE, dtl.getCapitalAssetTagNumber()); 1529 valid = false; 1530 } 1531 } 1532 1533 Map<String, Object> criteria = new HashMap<String, Object>(); 1534 criteria.put(KFSPropertyConstants.CAMPUS_CODE, dtl.getCampusCode()); 1535 Campus campus = (Campus) SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(Campus.class).getExternalizableBusinessObject(Campus.class, criteria); 1536 if (ObjectUtils.isNull(campus)) { 1537 valid = false; 1538 String label = this.getDataDictionaryService().getAttributeLabel(Campus.class, KFSPropertyConstants.CAMPUS_CODE); 1539 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.CAMPUS_CODE, KFSKeyConstants.ERROR_EXISTENCE, label); 1540 } 1541 1542 Map<String, String> params; 1543 params = new HashMap<String, String>(); 1544 params.put(KFSPropertyConstants.CAMPUS_CODE, dtl.getCampusCode()); 1545 params.put(KFSPropertyConstants.BUILDING_CODE, dtl.getBuildingCode()); 1546 Building building = (Building) this.getBusinessObjectService().findByPrimaryKey(Building.class, params); 1547 if (ObjectUtils.isNull(building)) { 1548 valid = false; 1549 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.BUILDING_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_BUILDING_CODE, dtl.getBuildingCode(), dtl.getCampusCode()); 1550 } 1551 1552 params = new HashMap<String, String>(); 1553 params.put(KFSPropertyConstants.CAMPUS_CODE, dtl.getCampusCode()); 1554 params.put(KFSPropertyConstants.BUILDING_CODE, dtl.getBuildingCode()); 1555 params.put(KFSPropertyConstants.BUILDING_ROOM_NUMBER, dtl.getBuildingRoomNumber()); 1556 Room room = (Room) this.getBusinessObjectService().findByPrimaryKey(Room.class, params); 1557 AssetType assetType = getAssetType(capitalAssetInformation.getCapitalAssetTypeCode()); 1558 if (ObjectUtils.isNull(room) && (ObjectUtils.isNull(assetType) || assetType.isMovingIndicator())) { 1559 valid = false; 1560 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPathPrefix + "[" + index + "]" + "." + KFSPropertyConstants.BUILDING_ROOM_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_ROOM_NUMBER, dtl.getBuildingRoomNumber(), dtl.getBuildingCode(), dtl.getCampusCode()); 1561 } 1562 index++; 1563 } 1564 1565 return valid; 1566 } 1567 1568 /** 1569 * Check assetTypeCode existence. 1570 * 1571 * @param assetTypeCode 1572 * @return 1573 */ 1574 public boolean isAssetTypeExisting(String assetTypeCode) { 1575 AssetType assetType = getAssetType(assetTypeCode); 1576 return ObjectUtils.isNotNull(assetType); 1577 } 1578 1579 /** 1580 * Retrieve the Asset type from the provided assetType Code 1581 * 1582 * @param assetTypeCode 1583 * @return {@link AssetType} 1584 */ 1585 protected AssetType getAssetType(String assetTypeCode) { 1586 Map<String, String> params = new HashMap<String, String>(); 1587 params.put(KFSPropertyConstants.CAPITAL_ASSET_TYPE_CODE, assetTypeCode); 1588 AssetType assetType = (AssetType) this.getBusinessObjectService().findByPrimaryKey(AssetType.class, params); 1589 return assetType; 1590 } 1591 1592 /** 1593 * Validate asset quantity the user entered matching the number of asset tag lines. 1594 * 1595 * @param capitalAssetInformation 1596 * @return 1597 */ 1598 protected boolean validateTotalNumberOfAssetTagLines(CapitalAssetInformation capitalAssetInformation) { 1599 boolean valid = true; 1600 Integer userInputAssetQuantity = capitalAssetInformation.getCapitalAssetQuantity(); 1601 if (userInputAssetQuantity != null && (ObjectUtils.isNull(capitalAssetInformation.getCapitalAssetInformationDetails()) || capitalAssetInformation.getCapitalAssetInformationDetails().isEmpty())) { 1602 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_QUANTITY, CabKeyConstants.CapitalAssetInformation.ERROR_ASSET_TAG_LINE_REQUIRED); 1603 valid = false; 1604 } 1605 else if (userInputAssetQuantity != null && userInputAssetQuantity.intValue() != capitalAssetInformation.getCapitalAssetInformationDetails().size()) { 1606 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CAPITAL_ASSET_QUANTITY, CabKeyConstants.CapitalAssetInformation.ERROR_ASSET_QUANTITY_NOT_MATCHING_TAG_LINES); 1607 valid = false; 1608 } 1609 1610 return valid; 1611 } 1612 1613 /** 1614 * To check if the tag number is duplicate or in use 1615 * 1616 * @param capitalAssetInformation, capitalAssetInformationDetail 1617 * @return boolean false if data is duplicate or in use 1618 */ 1619 protected boolean isTagNumberDuplicate(List<CapitalAssetInformationDetail> capitalAssetInformationDetails, CapitalAssetInformationDetail dtl) { 1620 boolean duplicateTag = false; 1621 int tagCounter = 0; 1622 if (!this.getAssetService().findActiveAssetsMatchingTagNumber(dtl.getCapitalAssetTagNumber()).isEmpty()) { 1623 // Tag number is already in use 1624 duplicateTag = true; 1625 } 1626 else { 1627 for (CapitalAssetInformationDetail capitalAssetInfoDetl : capitalAssetInformationDetails) { 1628 if (capitalAssetInfoDetl.getCapitalAssetTagNumber().equalsIgnoreCase(dtl.getCapitalAssetTagNumber().toString())) { 1629 tagCounter++; 1630 } 1631 } 1632 if (tagCounter > 1) { 1633 // Tag number already exists in the collection 1634 duplicateTag = true; 1635 } 1636 } 1637 return duplicateTag; 1638 } 1639 1640 /** 1641 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#notifyRouteStatusChange(java.lang.String, 1642 * java.lang.String) 1643 */ 1644 public void notifyRouteStatusChange(DocumentHeader documentHeader) { 1645 1646 KualiWorkflowDocument workflowDocument = documentHeader.getWorkflowDocument(); 1647 String documentNumber = documentHeader.getDocumentNumber(); 1648 1649 String documentType = workflowDocument.getDocumentType(); 1650 1651 if (workflowDocument.stateIsCanceled() || workflowDocument.stateIsDisapproved()) { 1652 // release CAB line items 1653 activateCabPOLines(documentNumber); 1654 activateCabGlLines(documentNumber); 1655 } 1656 if (workflowDocument.stateIsProcessed()) { 1657 // update CAB GL lines if fully processed 1658 updatePOLinesStatusAsProcessed(documentNumber); 1659 updateGlLinesStatusAsProcessed(documentNumber); 1660 // report asset numbers to PO 1661 Integer poId = getPurchaseOrderIdentifier(documentNumber); 1662 if (poId != null) { 1663 List<Long> assetNumbers = null; 1664 if (DocumentTypeName.ASSET_ADD_GLOBAL.equalsIgnoreCase(documentType)) { 1665 assetNumbers = getAssetNumbersFromAssetGlobal(documentNumber); 1666 } 1667 else if (DocumentTypeName.ASSET_PAYMENT.equalsIgnoreCase(documentType)) { 1668 assetNumbers = getAssetNumbersFromAssetPayment(documentNumber); 1669 } 1670 1671 if (!assetNumbers.isEmpty()) { 1672 String noteText = buildNoteTextForPurApDoc(documentType, assetNumbers); 1673 SpringContext.getBean(PurchasingAccountsPayableModuleService.class).addAssignedAssetNumbers(poId, workflowDocument.getInitiatorPrincipalId(), noteText); 1674 } 1675 } 1676 } 1677 1678 } 1679 1680 /** 1681 * update cab non-PO lines status code for item/account/glEntry to 'P' as fully processed when possible 1682 * 1683 * @param documentNumber 1684 */ 1685 protected void updateGlLinesStatusAsProcessed(String documentNumber) { 1686 Map<String, String> fieldValues = new HashMap<String, String>(); 1687 fieldValues.put(CabPropertyConstants.PurchasingAccountsPayableItemAsset.CAMS_DOCUMENT_NUMBER, documentNumber); 1688 Collection<GeneralLedgerEntryAsset> matchingGlAssets = this.getBusinessObjectService().findMatching(GeneralLedgerEntryAsset.class, fieldValues); 1689 if (matchingGlAssets != null && !matchingGlAssets.isEmpty()) { 1690 for (GeneralLedgerEntryAsset generalLedgerEntryAsset : matchingGlAssets) { 1691 GeneralLedgerEntry generalLedgerEntry = generalLedgerEntryAsset.getGeneralLedgerEntry(); 1692 1693 // update gl status as processed 1694 generalLedgerEntry.setActivityStatusCode(CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS); 1695 this.getBusinessObjectService().save(generalLedgerEntry); 1696 1697 // release asset lock 1698 if (isFpDocumentFullyProcessed(generalLedgerEntry)) { 1699 getCapitalAssetManagementModuleService().deleteAssetLocks(generalLedgerEntry.getDocumentNumber(), null); 1700 } 1701 } 1702 } 1703 } 1704 1705 /** 1706 * Check all generalLedgerEntries from the same FP document are fully processed. 1707 * 1708 * @param generalLedgerEntry 1709 * @return 1710 */ 1711 protected boolean isFpDocumentFullyProcessed(GeneralLedgerEntry generalLedgerEntry) { 1712 Map<String, String> fieldValues = new HashMap<String, String>(); 1713 fieldValues.put(CabPropertyConstants.GeneralLedgerEntry.DOCUMENT_NUMBER, generalLedgerEntry.getDocumentNumber()); 1714 Collection<GeneralLedgerEntry> matchingGlEntries = this.getBusinessObjectService().findMatching(GeneralLedgerEntry.class, fieldValues); 1715 1716 for (GeneralLedgerEntry glEntry : matchingGlEntries) { 1717 if (!CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS.equals(glEntry.getActivityStatusCode())) { 1718 return false; 1719 } 1720 } 1721 return true; 1722 } 1723 1724 /** 1725 * update CAB PO lines status code for item/account/glEntry to 'P' as fully processed when possible 1726 * 1727 * @param documentNumber 1728 */ 1729 protected void updatePOLinesStatusAsProcessed(String documentNumber) { 1730 Map<String, String> fieldValues = new HashMap<String, String>(); 1731 fieldValues.put(CabPropertyConstants.PurchasingAccountsPayableItemAsset.CAMS_DOCUMENT_NUMBER, documentNumber); 1732 Collection<PurchasingAccountsPayableItemAsset> matchingAssets = this.getBusinessObjectService().findMatching(PurchasingAccountsPayableItemAsset.class, fieldValues); 1733 if (matchingAssets != null && !matchingAssets.isEmpty()) { 1734 // Map<Long, GeneralLedgerEntry> updateGlLines = new HashMap<Long, GeneralLedgerEntry>(); 1735 // update item and account status code to 'P' as fully processed 1736 for (PurchasingAccountsPayableItemAsset itemAsset : matchingAssets) { 1737 1738 itemAsset.setActivityStatusCode(CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS); 1739 for (PurchasingAccountsPayableLineAssetAccount assetAccount : itemAsset.getPurchasingAccountsPayableLineAssetAccounts()) { 1740 assetAccount.setActivityStatusCode(CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS); 1741 GeneralLedgerEntry generalLedgerEntry = assetAccount.getGeneralLedgerEntry(); 1742 1743 // updateGlLines.put(generalLedgerEntry.getGeneralLedgerAccountIdentifier(), generalLedgerEntry); 1744 1745 if (isGlEntryFullyProcessed(generalLedgerEntry)) { 1746 generalLedgerEntry.setActivityStatusCode(CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS); 1747 this.getBusinessObjectService().save(generalLedgerEntry); 1748 } 1749 } 1750 1751 // update cab document status code to 'P' as all its items fully processed 1752 PurchasingAccountsPayableDocument purapDocument = itemAsset.getPurchasingAccountsPayableDocument(); 1753 if (isDocumentFullyProcessed(purapDocument)) { 1754 purapDocument.setActivityStatusCode(CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS); 1755 } 1756 1757 this.getBusinessObjectService().save(purapDocument); 1758 1759 String lockingInformation = null; 1760 PurchaseOrderDocument poDocument = getPurApInfoService().getCurrentDocumentForPurchaseOrderIdentifier(purapDocument.getPurchaseOrderIdentifier()); 1761 // Only individual system will lock on item line number. other system will using preq/cm doc nbr as the locking 1762 // key 1763 if (PurapConstants.CapitalAssetTabStrings.INDIVIDUAL_ASSETS.equalsIgnoreCase(poDocument.getCapitalAssetSystemTypeCode())) { 1764 lockingInformation = itemAsset.getAccountsPayableLineItemIdentifier().toString(); 1765 } 1766 // release the asset lock no matter if it's Asset global or Asset Payment since the CAB user can create Asset global 1767 // doc even if Purap Asset numbers existing. 1768 if (isAccountsPayableItemLineFullyProcessed(purapDocument, lockingInformation)) { 1769 getCapitalAssetManagementModuleService().deleteAssetLocks(purapDocument.getDocumentNumber(), lockingInformation); 1770 } 1771 1772 } 1773 } 1774 1775 } 1776 1777 /** 1778 * If lockingInformation is not empty, check all item lines from the same PurAp item are fully processed. If lockingInformation 1779 * is empty, we check all items from the same PREQ/CM document processed as fully processed. 1780 * 1781 * @param itemAsset 1782 * @return 1783 */ 1784 protected boolean isAccountsPayableItemLineFullyProcessed(PurchasingAccountsPayableDocument purapDocument, String lockingInformation) { 1785 for (PurchasingAccountsPayableItemAsset item : purapDocument.getPurchasingAccountsPayableItemAssets()) { 1786 if ((StringUtils.isBlank(lockingInformation) && !CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS.equals(item.getActivityStatusCode())) || (StringUtils.isNotBlank(lockingInformation) && item.getAccountsPayableLineItemIdentifier().equals(lockingInformation) && !CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS.equals(item.getActivityStatusCode()))) { 1787 return false; 1788 } 1789 } 1790 return true; 1791 } 1792 1793 /** 1794 * Check if Gl Entry related accounts are fully processed 1795 * 1796 * @param glEntry 1797 * @return 1798 */ 1799 protected boolean isGlEntryFullyProcessed(GeneralLedgerEntry glEntry) { 1800 for (PurchasingAccountsPayableLineAssetAccount account : glEntry.getPurApLineAssetAccounts()) { 1801 if (!CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS.equalsIgnoreCase(account.getActivityStatusCode())) { 1802 return false; 1803 } 1804 } 1805 return true; 1806 } 1807 1808 /** 1809 * Check if PurApDocument related items are fully processed. 1810 * 1811 * @param purapDocument 1812 * @return 1813 */ 1814 protected boolean isDocumentFullyProcessed(PurchasingAccountsPayableDocument purapDocument) { 1815 for (PurchasingAccountsPayableItemAsset item : purapDocument.getPurchasingAccountsPayableItemAssets()) { 1816 if (!CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS.equalsIgnoreCase(item.getActivityStatusCode())) { 1817 return false; 1818 } 1819 } 1820 return true; 1821 } 1822 1823 /** 1824 * Build the appropriate note text being set to the purchase order document. 1825 * 1826 * @param documentType 1827 * @param assetNumbers 1828 * @return 1829 */ 1830 protected String buildNoteTextForPurApDoc(String documentType, List<Long> assetNumbers) { 1831 StringBuffer noteText = new StringBuffer(); 1832 1833 if (DocumentTypeName.ASSET_ADD_GLOBAL.equalsIgnoreCase(documentType)) { 1834 noteText.append("Asset Numbers have been created for this document: "); 1835 } 1836 else { 1837 noteText.append("Existing Asset Numbers have been applied for this document: "); 1838 } 1839 1840 for (int i = 0; i < assetNumbers.size(); i++) { 1841 noteText.append(assetNumbers.get(i).toString()); 1842 if (i < assetNumbers.size() - 1) { 1843 noteText.append(", "); 1844 } 1845 } 1846 1847 return noteText.toString(); 1848 } 1849 1850 /** 1851 * Acquire asset numbers from CAMS asset payment document. 1852 * 1853 * @param documentNumber 1854 * @param assetNumbers 1855 */ 1856 protected List<Long> getAssetNumbersFromAssetGlobal(String documentNumber) { 1857 List<Long> assetNumbers = new ArrayList<Long>(); 1858 Map<String, String> fieldValues = new HashMap<String, String>(); 1859 fieldValues.put(CamsPropertyConstants.AssetGlobalDetail.DOCUMENT_NUMBER, documentNumber); 1860 Collection<AssetGlobalDetail> assetGlobalDetails = this.getBusinessObjectService().findMatching(AssetGlobalDetail.class, fieldValues); 1861 for (AssetGlobalDetail detail : assetGlobalDetails) { 1862 assetNumbers.add(detail.getCapitalAssetNumber()); 1863 } 1864 return assetNumbers; 1865 } 1866 1867 /** 1868 * Acquire asset numbers from CAMS asset global document. 1869 * 1870 * @param documentNumber 1871 * @param assetNumbers 1872 */ 1873 protected List<Long> getAssetNumbersFromAssetPayment(String documentNumber) { 1874 List<Long> assetNumbers = new ArrayList<Long>(); 1875 Map<String, String> fieldValues = new HashMap<String, String>(); 1876 fieldValues.put(CamsPropertyConstants.DOCUMENT_NUMBER, documentNumber); 1877 Collection<AssetPaymentAssetDetail> paymentAssetDetails = this.getBusinessObjectService().findMatching(AssetPaymentAssetDetail.class, fieldValues); 1878 for (AssetPaymentAssetDetail detail : paymentAssetDetails) { 1879 if (ObjectUtils.isNotNull(detail.getAsset())) { 1880 assetNumbers.add(detail.getCapitalAssetNumber()); 1881 } 1882 } 1883 1884 return assetNumbers; 1885 } 1886 1887 /** 1888 * Query PurchasingAccountsPayableItemAsset and return the purchaseOrderIdentifier if the given documentNumber is initiated from 1889 * the PurAp line. 1890 * 1891 * @param documentNumber 1892 * @return 1893 */ 1894 protected Integer getPurchaseOrderIdentifier(String camsDocumentNumber) { 1895 Map<String, String> fieldValues = new HashMap<String, String>(); 1896 fieldValues.put(CabPropertyConstants.PurchasingAccountsPayableItemAsset.CAMS_DOCUMENT_NUMBER, camsDocumentNumber); 1897 Collection<PurchasingAccountsPayableItemAsset> matchingItems = this.getBusinessObjectService().findMatching(PurchasingAccountsPayableItemAsset.class, fieldValues); 1898 1899 for (PurchasingAccountsPayableItemAsset item : matchingItems) { 1900 if (ObjectUtils.isNull(item.getPurchasingAccountsPayableDocument())) { 1901 item.refreshReferenceObject(CabPropertyConstants.PurchasingAccountsPayableItemAsset.PURCHASING_ACCOUNTS_PAYABLE_DOCUMENT); 1902 } 1903 return item.getPurchasingAccountsPayableDocument().getPurchaseOrderIdentifier(); 1904 } 1905 return null; 1906 } 1907 1908 /** 1909 * @see org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService#getCurrentPurchaseOrderDocumentNumber(java.lang.String) 1910 */ 1911 public String getCurrentPurchaseOrderDocumentNumber(String camsDocumentNumber) { 1912 Integer poId = getPurchaseOrderIdentifier(camsDocumentNumber); 1913 if (poId != null) { 1914 PurchaseOrderDocument poDocument = getPurApInfoService().getCurrentDocumentForPurchaseOrderIdentifier(poId); 1915 if (ObjectUtils.isNotNull(poDocument)) { 1916 return poDocument.getDocumentNumber(); 1917 } 1918 } 1919 return null; 1920 } 1921 1922 /** 1923 * Activates CAB GL Lines 1924 * 1925 * @param documentNumber String 1926 */ 1927 protected void activateCabGlLines(String documentNumber) { 1928 Map<String, String> fieldValues = new HashMap<String, String>(); 1929 fieldValues.put(CabPropertyConstants.PurchasingAccountsPayableItemAsset.CAMS_DOCUMENT_NUMBER, documentNumber); 1930 Collection<GeneralLedgerEntryAsset> matchingGlAssets = this.getBusinessObjectService().findMatching(GeneralLedgerEntryAsset.class, fieldValues); 1931 if (matchingGlAssets != null && !matchingGlAssets.isEmpty()) { 1932 for (GeneralLedgerEntryAsset generalLedgerEntryAsset : matchingGlAssets) { 1933 GeneralLedgerEntry generalLedgerEntry = generalLedgerEntryAsset.getGeneralLedgerEntry(); 1934 generalLedgerEntry.setTransactionLedgerSubmitAmount(KualiDecimal.ZERO); 1935 generalLedgerEntry.setActivityStatusCode(CabConstants.ActivityStatusCode.NEW); 1936 this.getBusinessObjectService().save(generalLedgerEntry); 1937 this.getBusinessObjectService().delete(generalLedgerEntryAsset); 1938 } 1939 } 1940 } 1941 1942 /** 1943 * Activates PO Lines 1944 * 1945 * @param documentNumber String 1946 */ 1947 protected void activateCabPOLines(String documentNumber) { 1948 Map<String, String> fieldValues = new HashMap<String, String>(); 1949 fieldValues.put(CabPropertyConstants.PurchasingAccountsPayableItemAsset.CAMS_DOCUMENT_NUMBER, documentNumber); 1950 Collection<PurchasingAccountsPayableItemAsset> matchingPoAssets = this.getBusinessObjectService().findMatching(PurchasingAccountsPayableItemAsset.class, fieldValues); 1951 1952 if (matchingPoAssets != null && !matchingPoAssets.isEmpty()) { 1953 for (PurchasingAccountsPayableItemAsset itemAsset : matchingPoAssets) { 1954 PurchasingAccountsPayableDocument purapDocument = itemAsset.getPurchasingAccountsPayableDocument(); 1955 purapDocument.setActivityStatusCode(CabConstants.ActivityStatusCode.MODIFIED); 1956 this.getBusinessObjectService().save(purapDocument); 1957 itemAsset.setActivityStatusCode(CabConstants.ActivityStatusCode.MODIFIED); 1958 this.getBusinessObjectService().save(itemAsset); 1959 List<PurchasingAccountsPayableLineAssetAccount> lineAssetAccounts = itemAsset.getPurchasingAccountsPayableLineAssetAccounts(); 1960 for (PurchasingAccountsPayableLineAssetAccount assetAccount : lineAssetAccounts) { 1961 assetAccount.setActivityStatusCode(CabConstants.ActivityStatusCode.MODIFIED); 1962 this.getBusinessObjectService().save(assetAccount); 1963 GeneralLedgerEntry generalLedgerEntry = assetAccount.getGeneralLedgerEntry(); 1964 KualiDecimal submitAmount = generalLedgerEntry.getTransactionLedgerSubmitAmount(); 1965 if (submitAmount == null) { 1966 submitAmount = KualiDecimal.ZERO; 1967 } 1968 submitAmount = submitAmount.subtract(assetAccount.getItemAccountTotalAmount()); 1969 generalLedgerEntry.setTransactionLedgerSubmitAmount(submitAmount); 1970 generalLedgerEntry.setActivityStatusCode(CabConstants.ActivityStatusCode.MODIFIED); 1971 this.getBusinessObjectService().save(generalLedgerEntry); 1972 } 1973 } 1974 } 1975 } 1976 1977 1978 /** 1979 * gets the document type based on the instance of a class 1980 * 1981 * @param accountingDocument 1982 * @return 1983 */ 1984 protected String getDocumentTypeName(AccountingDocument accountingDocument) { 1985 String documentTypeName = null; 1986 if (accountingDocument instanceof YearEndGeneralErrorCorrectionDocument) 1987 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.YEAR_END_GENERAL_ERROR_CORRECTION; 1988 else if (accountingDocument instanceof YearEndDistributionOfIncomeAndExpenseDocument) 1989 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.YEAR_END_DISTRIBUTION_OF_INCOME_AND_EXPENSE; 1990 else if (accountingDocument instanceof ServiceBillingDocument) 1991 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.SERVICE_BILLING; 1992 else if (accountingDocument instanceof GeneralErrorCorrectionDocument) 1993 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.GENERAL_ERROR_CORRECTION; 1994 else if (accountingDocument instanceof CashReceiptDocument) 1995 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.CASH_RECEIPT; 1996 else if (accountingDocument instanceof AdvanceDepositDocument) 1997 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.ADVANCE_DEPOSIT; 1998 else if (accountingDocument instanceof CreditCardReceiptDocument) 1999 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.CREDIT_CARD_RECEIPT; 2000 else if (accountingDocument instanceof DistributionOfIncomeAndExpenseDocument) 2001 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.DISTRIBUTION_OF_INCOME_AND_EXPENSE; 2002 else if (accountingDocument instanceof InternalBillingDocument) 2003 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.INTERNAL_BILLING; 2004 else if (accountingDocument instanceof ProcurementCardDocument) 2005 documentTypeName = KFSConstants.FinancialDocumentTypeCodes.PROCUREMENT_CARD; 2006 else 2007 throw new RuntimeException("Invalid FP document type."); 2008 2009 return documentTypeName; 2010 } 2011 2012 public ParameterService getParameterService() { 2013 return SpringContext.getBean(ParameterService.class); 2014 } 2015 2016 public BusinessObjectService getBusinessObjectService() { 2017 return SpringContext.getBean(BusinessObjectService.class); 2018 } 2019 2020 public DataDictionaryService getDataDictionaryService() { 2021 return SpringContext.getBean(DataDictionaryService.class); 2022 } 2023 2024 public AssetService getAssetService() { 2025 return SpringContext.getBean(AssetService.class); 2026 } 2027 2028 public KualiModuleService getKualiModuleService() { 2029 return SpringContext.getBean(KualiModuleService.class); 2030 } 2031 2032 public CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() { 2033 return SpringContext.getBean(CapitalAssetManagementModuleService.class); 2034 } 2035 2036 protected PurApInfoService getPurApInfoService() { 2037 return SpringContext.getBean(PurApInfoService.class); 2038 } 2039 2040 }