001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.module.cam.document.service.impl; 017 018 import java.beans.PropertyDescriptor; 019 import java.lang.reflect.InvocationTargetException; 020 import java.lang.reflect.Method; 021 import java.util.ArrayList; 022 import java.util.Arrays; 023 import java.util.HashMap; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.regex.Pattern; 027 028 import org.apache.commons.beanutils.PropertyUtils; 029 import org.apache.commons.lang.StringUtils; 030 import org.kuali.kfs.coa.businessobject.ObjectCode; 031 import org.kuali.kfs.coa.service.ObjectCodeService; 032 import org.kuali.kfs.module.cam.CamsConstants; 033 import org.kuali.kfs.module.cam.CamsKeyConstants; 034 import org.kuali.kfs.module.cam.CamsPropertyConstants; 035 import org.kuali.kfs.module.cam.businessobject.Asset; 036 import org.kuali.kfs.module.cam.businessobject.AssetGlobal; 037 import org.kuali.kfs.module.cam.businessobject.AssetPayment; 038 import org.kuali.kfs.module.cam.businessobject.AssetPaymentAssetDetail; 039 import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail; 040 import org.kuali.kfs.module.cam.document.AssetPaymentDocument; 041 import org.kuali.kfs.module.cam.document.dataaccess.AssetPaymentDao; 042 import org.kuali.kfs.module.cam.document.service.AssetGlobalService; 043 import org.kuali.kfs.module.cam.document.service.AssetPaymentService; 044 import org.kuali.kfs.module.cam.document.service.AssetRetirementService; 045 import org.kuali.kfs.module.cam.document.service.AssetService; 046 import org.kuali.kfs.module.cam.util.AssetPaymentDistributor; 047 import org.kuali.kfs.sys.KFSPropertyConstants; 048 import org.kuali.kfs.sys.businessobject.UniversityDate; 049 import org.kuali.kfs.sys.context.SpringContext; 050 import org.kuali.kfs.sys.service.UniversityDateService; 051 import org.kuali.kfs.sys.service.impl.KfsParameterConstants; 052 import org.kuali.rice.kns.bo.PersistableBusinessObject; 053 import org.kuali.rice.kns.service.BusinessObjectService; 054 import org.kuali.rice.kns.service.ParameterService; 055 import org.kuali.rice.kns.util.GlobalVariables; 056 import org.kuali.rice.kns.util.KualiDecimal; 057 import org.kuali.rice.kns.util.ObjectUtils; 058 import org.springframework.transaction.annotation.Transactional; 059 060 @Transactional 061 public class AssetPaymentServiceImpl implements AssetPaymentService { 062 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetPaymentServiceImpl.class); 063 064 private BusinessObjectService businessObjectService; 065 private AssetPaymentDao assetPaymentDao; 066 private ParameterService parameterService; 067 private UniversityDateService universityDateService; 068 private ObjectCodeService objectCodeService; 069 private AssetRetirementService assetRetirementService; 070 private AssetService assetService; 071 private AssetGlobalService assetGlobalService; 072 073 /** 074 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#getMaxSequenceNumber(org.kuali.kfs.module.cam.businessobject.AssetPayment) 075 */ 076 public Integer getMaxSequenceNumber(Long capitalAssetNumber) { 077 return this.getAssetPaymentDao().getMaxSquenceNumber(capitalAssetNumber); 078 } 079 080 /** 081 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#isPaymentFederalOwned(org.kuali.kfs.module.cam.businessobject.AssetPayment) 082 */ 083 public boolean isPaymentFederalOwned(AssetPayment assetPayment) { 084 assetPayment.refreshReferenceObject(CamsPropertyConstants.AssetPayment.FINANCIAL_OBJECT); 085 if (ObjectUtils.isNotNull(assetPayment.getFinancialObject())) { 086 return this.getParameterService().getParameterValues(Asset.class, CamsConstants.Parameters.FEDERAL_OWNED_OBJECT_SUB_TYPES).contains(assetPayment.getFinancialObject().getFinancialObjectSubTypeCode()); 087 } 088 return false; 089 } 090 091 092 /** 093 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#isPaymentEligibleForAccumDeprGLPosting(org.kuali.kfs.module.cam.businessobject.AssetPayment) 094 */ 095 public boolean isPaymentEligibleForAccumDeprGLPosting(AssetPayment assetPayment) { 096 KualiDecimal accumlatedDepreciationAmount = assetPayment.getAccumulatedPrimaryDepreciationAmount(); 097 return accumlatedDepreciationAmount == null ? false : !accumlatedDepreciationAmount.isZero(); 098 } 099 100 /** 101 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#isPaymentEligibleForCapitalizationGLPosting(org.kuali.kfs.module.cam.businessobject.AssetPayment) 102 */ 103 public boolean isPaymentEligibleForCapitalizationGLPosting(AssetPayment assetPayment) { 104 KualiDecimal accountChargeAmount = assetPayment.getAccountChargeAmount(); 105 return accountChargeAmount == null ? false : !accountChargeAmount.isZero(); 106 } 107 108 /** 109 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#isPaymentEligibleForOffsetGLPosting(org.kuali.kfs.module.cam.businessobject.AssetPayment) 110 */ 111 public boolean isPaymentEligibleForOffsetGLPosting(AssetPayment assetPayment) { 112 KualiDecimal accountChargeAmount = assetPayment.getAccountChargeAmount(); 113 if (assetPayment.getAccumulatedPrimaryDepreciationAmount() == null) { 114 assetPayment.setAccumulatedPrimaryDepreciationAmount(KualiDecimal.ZERO); 115 } 116 KualiDecimal accumlatedDepreciationAmount = assetPayment.getAccumulatedPrimaryDepreciationAmount(); 117 return accountChargeAmount == null ? false : !accountChargeAmount.subtract(accumlatedDepreciationAmount).isZero(); 118 } 119 120 /** 121 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#isPaymentFinancialObjectActive(org.kuali.kfs.module.cam.businessobject.AssetPayment) 122 */ 123 public boolean isPaymentFinancialObjectActive(AssetPayment assetPayment) { 124 ObjectCode financialObjectCode = new ObjectCode(); 125 financialObjectCode.setUniversityFiscalYear(getUniversityDateService().getCurrentFiscalYear()); 126 financialObjectCode.setChartOfAccountsCode(assetPayment.getChartOfAccountsCode()); 127 financialObjectCode.setFinancialObjectCode(assetPayment.getFinancialObjectCode()); 128 financialObjectCode = (ObjectCode) getBusinessObjectService().retrieve(financialObjectCode); 129 if (ObjectUtils.isNotNull(financialObjectCode)) { 130 return financialObjectCode.isActive(); 131 } 132 return false; 133 } 134 135 136 /** 137 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#processApprovedAssetPayment(org.kuali.kfs.module.cam.document.AssetPaymentDocument) 138 */ 139 public void processApprovedAssetPayment(AssetPaymentDocument document) { 140 // Creating new asset payment records 141 processPayments(document); 142 } 143 144 145 /** 146 * Creates a new asset payment record for each new asset payment detail record and then save them 147 * 148 * @param document 149 */ 150 protected void processPayments(AssetPaymentDocument document) { 151 List<AssetPaymentDetail> assetPaymentDetailLines = document.getSourceAccountingLines(); 152 List<AssetPaymentAssetDetail> assetPaymentAssetDetails = document.getAssetPaymentAssetDetail(); 153 List<PersistableBusinessObject> assetPayments = new ArrayList<PersistableBusinessObject>(); 154 Integer maxSequenceNo = new Integer(0); 155 156 //instantiate asset payment distributor 157 AssetPaymentDistributor paymentDistributor = document.getAssetPaymentDistributor(); 158 159 // Calculating the asset payments distributions for each individual asset on the list 160 Map<String, Map<AssetPaymentAssetDetail, KualiDecimal>> assetPaymentDistributionMap = paymentDistributor.getAssetPaymentDistributions(); 161 162 try { 163 // Creating a new payment record for each asset that has payments. 164 for (AssetPaymentAssetDetail assetPaymentAssetDetail : assetPaymentAssetDetails) { 165 166 maxSequenceNo = getMaxSequenceNumber(assetPaymentAssetDetail.getCapitalAssetNumber()); 167 168 KualiDecimal totalAmount = KualiDecimal.ZERO; 169 for (AssetPaymentDetail assetPaymentDetail : assetPaymentDetailLines) { 170 171 // Retrieve the asset payment from the distribution map 172 KualiDecimal amount = assetPaymentDistributionMap.get(assetPaymentDetail.getAssetPaymentDetailKey()).get(assetPaymentAssetDetail); 173 totalAmount = totalAmount.add(amount); 174 175 AssetPayment assetPayment = new AssetPayment(assetPaymentDetail); 176 assetPayment.setCapitalAssetNumber(assetPaymentAssetDetail.getCapitalAssetNumber()); 177 assetPayment.setTransferPaymentCode(CamsConstants.AssetPayment.TRANSFER_PAYMENT_CODE_N); 178 assetPayment.setPaymentSequenceNumber(++maxSequenceNo); 179 if (StringUtils.isBlank(assetPayment.getDocumentNumber())) { 180 assetPayment.setDocumentNumber(document.getDocumentNumber()); 181 } 182 assetPayment.setAccountChargeAmount(amount); 183 184 KualiDecimal baseAmount = KualiDecimal.ZERO; 185 186 // If the object sub type is not in the list of federally owned object sub types, then... 187 ObjectCode objectCode = this.getObjectCodeService().getByPrimaryId(assetPaymentDetail.getPostingYear(), assetPaymentDetail.getChartOfAccountsCode(), assetPaymentDetail.getFinancialObjectCode()); 188 189 // Depreciation Base Amount will be assigned to each payment only when the object code's sub type code is not a 190 // federally owned one 191 if (!this.isNonDepreciableFederallyOwnedObjSubType(objectCode.getFinancialObjectSubTypeCode())) { 192 baseAmount = baseAmount.add(amount); 193 } 194 assetPayment.setPrimaryDepreciationBaseAmount(baseAmount); 195 196 // Resetting each period field its value with nulls 197 this.adjustPaymentAmounts(assetPayment, false, true); 198 199 // add new payment 200 assetPayments.add(assetPayment); 201 } 202 // *********************BEGIN - Updating Asset *********************************************************** 203 // Retrieving the asset that will have its cost updated 204 HashMap<String, Long> keys = new HashMap<String, Long>(); 205 keys.put(CamsPropertyConstants.Asset.CAPITAL_ASSET_NUMBER, assetPaymentAssetDetail.getCapitalAssetNumber()); 206 Asset asset = (Asset) getBusinessObjectService().findByPrimaryKey(Asset.class, keys); 207 208 // Set previous total cost 209 if (assetPaymentAssetDetail.getPreviousTotalCostAmount() == null) { 210 assetPaymentAssetDetail.setPreviousTotalCostAmount(new KualiDecimal(0)); 211 } 212 213 // Setting the asset's new cost. 214 asset.setTotalCostAmount(assetPaymentAssetDetail.getPreviousTotalCostAmount().add(totalAmount)); 215 216 // Setting the asset's financial object sub-type Code. Only when the asset doesn't have one. 217 if (asset.getFinancialObjectSubTypeCode() == null || asset.getFinancialObjectSubTypeCode().trim().equals("")) { 218 asset.setFinancialObjectSubTypeCode(assetPaymentDetailLines.get(0).getObjectCode().getFinancialObjectSubTypeCode()); 219 } 220 // Saving changes 221 getBusinessObjectService().save(asset); 222 // *********************END - Updating Asset *********************************************************** 223 } 224 } 225 catch (Exception e) { 226 throw new RuntimeException(e); 227 } 228 // Finally, saving all the asset payment records. 229 this.getBusinessObjectService().save(assetPayments); 230 } 231 232 233 /** 234 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#adjustPaymentAmounts(org.kuali.kfs.module.cam.businessobject.AssetPayment, 235 * boolean, boolean) 236 */ 237 public void adjustPaymentAmounts(AssetPayment assetPayment, boolean reverseAmount, boolean nullPeriodDepreciation) throws IllegalAccessException, InvocationTargetException { 238 LOG.debug("Starting - adjustAmounts() "); 239 PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(AssetPayment.class); 240 for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { 241 Method readMethod = propertyDescriptor.getReadMethod(); 242 if (readMethod != null && propertyDescriptor.getPropertyType() != null && KualiDecimal.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { 243 KualiDecimal amount = (KualiDecimal) readMethod.invoke(assetPayment); 244 Method writeMethod = propertyDescriptor.getWriteMethod(); 245 if (writeMethod != null && amount != null) { 246 // Reset periodic depreciation expenses 247 if (nullPeriodDepreciation && Pattern.matches(CamsConstants.SET_PERIOD_DEPRECIATION_AMOUNT_REGEX, writeMethod.getName().toLowerCase())) { 248 Object[] nullVal = new Object[] { null }; 249 writeMethod.invoke(assetPayment, nullVal); 250 } 251 else if (reverseAmount) { 252 // reverse the amounts 253 writeMethod.invoke(assetPayment, (amount.negated())); 254 } 255 } 256 257 } 258 } 259 LOG.debug("Finished - adjustAmounts()"); 260 } 261 262 263 /** 264 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#isPaymentEligibleForGLPosting(org.kuali.kfs.module.cam.businessobject.AssetPayment) 265 */ 266 public boolean isPaymentEligibleForGLPosting(AssetPayment assetPayment) { 267 // Transfer payment code flag is not Y 268 boolean isEligible = !CamsConstants.AssetPayment.TRANSFER_PAYMENT_CODE_Y.equals(assetPayment.getTransferPaymentCode()); 269 // Financial object code is currently active 270 isEligible &= isPaymentFinancialObjectActive(assetPayment); 271 // Payment is not federally funded 272 isEligible &= !isPaymentFederalOwned(assetPayment); 273 return isEligible; 274 } 275 276 /** 277 * Checks if object sub type is a non-depreciable federally owned object sub type 278 * 279 * @param string objectSubType2 280 * @return true if is NON_DEPRECIABLE_FEDERALLY_OWNED_OBJECT_SUB_TYPES 281 */ 282 public boolean isNonDepreciableFederallyOwnedObjSubType(String objectSubType) { 283 List<String> federallyOwnedObjectSubTypes = new ArrayList<String>(); 284 if (this.getParameterService().parameterExists(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.NON_DEPRECIABLE_FEDERALLY_OWNED_OBJECT_SUB_TYPES)) { 285 federallyOwnedObjectSubTypes = this.getParameterService().getParameterValues(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.NON_DEPRECIABLE_FEDERALLY_OWNED_OBJECT_SUB_TYPES); 286 } 287 return federallyOwnedObjectSubTypes.contains(objectSubType); 288 } 289 290 /** 291 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#extractPostedDatePeriod(org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail) 292 */ 293 public boolean extractPostedDatePeriod(AssetPaymentDetail assetPaymentDetail) { 294 boolean valid = true; 295 Map<String, Object> primaryKeys = new HashMap<String, Object>(); 296 primaryKeys.put(KFSPropertyConstants.UNIVERSITY_DATE, assetPaymentDetail.getExpenditureFinancialDocumentPostedDate()); 297 UniversityDate universityDate = (UniversityDate) businessObjectService.findByPrimaryKey(UniversityDate.class, primaryKeys); 298 if (universityDate != null) { 299 assetPaymentDetail.setPostingYear(universityDate.getUniversityFiscalYear()); 300 assetPaymentDetail.setPostingPeriodCode(universityDate.getUniversityFiscalAccountingPeriod()); 301 return true; 302 } 303 else { 304 return false; 305 } 306 } 307 308 /** 309 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#getAssetPaymentDetailQuantity(org.kuali.kfs.module.cam.businessobject.AssetGlobal) 310 */ 311 public Integer getAssetPaymentDetailQuantity(AssetGlobal assetGlobal) { 312 Integer assetPaymentDetailQuantity = 0; 313 for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) { 314 assetPaymentDetailQuantity++; 315 } 316 return assetPaymentDetailQuantity; 317 } 318 319 320 /** 321 * @see org.kuali.kfs.module.cam.document.service.AssetPaymentService#validateAssets(java.lang.String, 322 * org.kuali.kfs.module.cam.businessobject.Asset) 323 */ 324 public boolean validateAssets(String errorPath, Asset asset) { 325 boolean valid = true; 326 327 // Validating the asset is a capital asset 328 if (!this.getAssetService().isCapitalAsset(asset)) { 329 GlobalVariables.getMessageMap().putError(errorPath, CamsKeyConstants.Payment.ERROR_NON_CAPITAL_ASSET, asset.getCapitalAssetNumber().toString()); 330 valid &= false; 331 } 332 333 // Validating the asset hasn't been retired 334 if (this.getAssetService().isAssetRetired(asset)) { 335 GlobalVariables.getMessageMap().putError(errorPath, CamsKeyConstants.Retirement.ERROR_NON_ACTIVE_ASSET_RETIREMENT, asset.getCapitalAssetNumber().toString()); 336 valid &= false; 337 } 338 return valid; 339 } 340 341 342 /** 343 * This method determines whether or not an asset has different object sub type codes in its documents. 344 * 345 * @return true when the asset has payments with object codes that point to different object sub type codes 346 */ 347 public boolean hasDifferentObjectSubTypes(AssetPaymentDocument document) { 348 List<String> subTypes = new ArrayList<String>(); 349 subTypes = SpringContext.getBean(ParameterService.class).getParameterValues(Asset.class, CamsConstants.Parameters.OBJECT_SUB_TYPE_GROUPS); 350 351 List<AssetPaymentDetail> assetPaymentDetails = document.getSourceAccountingLines(); 352 List<String> validObjectSubTypes = new ArrayList<String>(); 353 354 String objectSubTypeCode = null; 355 356 /* 357 * Expected system parameter elements (object sub types). [BD,BF] [CM,CF,CO] [UC,UF,UO] [LI,LF] 358 */ 359 360 // Getting the list of object sub type codes from the asset payments on the jsp. 361 List<String> objectSubTypeList = new ArrayList<String>(); 362 for (AssetPaymentDetail assetPaymentDetail : assetPaymentDetails) { 363 assetPaymentDetail.refreshReferenceObject(CamsPropertyConstants.AssetPaymentDetail.OBJECT_CODE); 364 if (ObjectUtils.isNull(assetPaymentDetail.getObjectCode())) { 365 return false; 366 } 367 objectSubTypeList.add(assetPaymentDetail.getObjectCode().getFinancialObjectSubTypeCode()); 368 } 369 370 if (!SpringContext.getBean(AssetService.class).isObjectSubTypeCompatible(objectSubTypeList)) { 371 return true; 372 } 373 374 List<AssetPaymentAssetDetail> assetPaymentAssetDetails = document.getAssetPaymentAssetDetail(); 375 for (AssetPaymentAssetDetail assetPaymentAssetDetail : assetPaymentAssetDetails) { 376 assetPaymentAssetDetail.getAsset().refreshReferenceObject(CamsPropertyConstants.Asset.ASSET_PAYMENTS); 377 List<AssetPayment> assetPayments = assetPaymentAssetDetail.getAsset().getAssetPayments(); 378 379 // Comparing against the already approved asset payments 380 if (!assetPayments.isEmpty()) { 381 for (AssetPayment assetPayment : assetPayments) { 382 String paymentSubObjectType = assetPayment.getFinancialObject().getFinancialObjectSubTypeCode(); 383 384 validObjectSubTypes = new ArrayList<String>(); 385 for (String subType : subTypes) { 386 validObjectSubTypes = Arrays.asList(StringUtils.split(subType, ",")); 387 if (validObjectSubTypes.contains(paymentSubObjectType)) { 388 break; 389 } 390 validObjectSubTypes = new ArrayList<String>(); 391 } 392 if (validObjectSubTypes.isEmpty()) 393 validObjectSubTypes.add(paymentSubObjectType); 394 395 // Comparing the same asset payment document 396 for (AssetPaymentDetail assetPaymentDetail : assetPaymentDetails) { 397 if (!validObjectSubTypes.contains(assetPaymentDetail.getObjectCode().getFinancialObjectSubTypeCode())) { 398 // Differences where found. 399 return true; 400 } 401 } 402 } 403 } 404 } 405 // If none object sub types are different... 406 return false; 407 } 408 409 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 410 this.businessObjectService = businessObjectService; 411 } 412 413 public BusinessObjectService getBusinessObjectService() { 414 return businessObjectService; 415 } 416 417 public ParameterService getParameterService() { 418 return parameterService; 419 } 420 421 422 public void setParameterService(ParameterService parameterService) { 423 this.parameterService = parameterService; 424 } 425 426 427 public AssetPaymentDao getAssetPaymentDao() { 428 return assetPaymentDao; 429 } 430 431 432 public void setAssetPaymentDao(AssetPaymentDao assetPaymentDao) { 433 this.assetPaymentDao = assetPaymentDao; 434 } 435 436 public ObjectCodeService getObjectCodeService() { 437 return objectCodeService; 438 } 439 440 public void setObjectCodeService(ObjectCodeService objectCodeService) { 441 this.objectCodeService = objectCodeService; 442 } 443 444 public UniversityDateService getUniversityDateService() { 445 return universityDateService; 446 } 447 448 public void setUniversityDateService(UniversityDateService universityDateService) { 449 this.universityDateService = universityDateService; 450 } 451 452 public AssetRetirementService getAssetRetirementService() { 453 return assetRetirementService; 454 } 455 456 public void setAssetRetirementService(AssetRetirementService assetRetirementService) { 457 this.assetRetirementService = assetRetirementService; 458 } 459 460 public AssetService getAssetService() { 461 return assetService; 462 } 463 464 public void setAssetService(AssetService assetService) { 465 this.assetService = assetService; 466 } 467 468 }