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 }