/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.module.cam.document;
import java.sql.Date;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.Account;
import org.kuali.kfs.coa.businessobject.ObjectCode;
import org.kuali.kfs.coa.service.AccountService;
import org.kuali.kfs.coa.service.ObjectCodeService;
import org.kuali.kfs.gl.GeneralLedgerConstants;
import org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService;
import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService;
import org.kuali.kfs.module.cam.CamsConstants;
import org.kuali.kfs.module.cam.CamsPropertyConstants;
import org.kuali.kfs.module.cam.businessobject.Asset;
import org.kuali.kfs.module.cam.businessobject.AssetDepreciationConvention;
import org.kuali.kfs.module.cam.businessobject.AssetGlobal;
import org.kuali.kfs.module.cam.businessobject.AssetGlobalDetail;
import org.kuali.kfs.module.cam.businessobject.AssetOrganization;
import org.kuali.kfs.module.cam.businessobject.AssetPayment;
import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail;
import org.kuali.kfs.module.cam.businessobject.AssetType;
import org.kuali.kfs.module.cam.businessobject.defaultvalue.NextAssetNumberFinder;
import org.kuali.kfs.module.cam.document.gl.AssetGlobalGeneralLedgerPendingEntrySource;
import org.kuali.kfs.module.cam.document.service.AssetDateService;
import org.kuali.kfs.module.cam.document.service.AssetGlobalService;
import org.kuali.kfs.module.cam.document.validation.impl.AssetGlobalRule;
import org.kuali.kfs.module.cam.util.KualiDecimalUtils;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.LedgerPostingMaintainable;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.parameter.ParameterEvaluator;
import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kns.document.MaintenanceDocument;
import org.kuali.rice.krad.bo.DocumentHeader;
import org.kuali.rice.krad.bo.PersistableBusinessObject;
import org.kuali.rice.krad.maintenance.MaintenanceLock;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* This class overrides the base {@link KualiGlobalMaintainableImpl} to generate the specific maintenance locks for Global assets
*/
public class AssetGlobalMaintainableImpl extends LedgerPostingMaintainable {
private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetGlobalMaintainableImpl.class);
protected static final String REQUIRES_REVIEW = "RequiresReview";
/**
* Lock on purchase order document since post processor will update PO document by adding notes.
*
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#getWorkflowEngineDocumentIdsToLock()
*/
@Override
public List<String> getWorkflowEngineDocumentIdsToLock() {
AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
if (ObjectUtils.isNotNull(assetGlobal) && assetGlobal.isCapitalAssetBuilderOriginIndicator()) {
String poDocId = SpringContext.getBean(CapitalAssetBuilderModuleService.class).getCurrentPurchaseOrderDocumentNumber(getDocumentNumber());
if (StringUtils.isNotBlank(poDocId)) {
List<String> documentIds = new ArrayList<String>();
documentIds.add(poDocId);
return documentIds;
}
}
return null;
}
/**
* If the Add Asset Global document is submit from CAB, bypass all the approvers.
*/
@Override
protected boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
if (REQUIRES_REVIEW.equals(nodeName)) {
return !isAccountAndOrganizationReviewRequired();
}
throw new UnsupportedOperationException("Cannot answer split question for this node you call \"" + nodeName + "\"");
}
/**
* check whether or not isCapitalAssetBuilderOriginIndicator
*/
protected boolean isAccountAndOrganizationReviewRequired(){
return ((AssetGlobal) getBusinessObject()).isCapitalAssetBuilderOriginIndicator();
}
/**
* Get Asset from AssetGlobal
*
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterNew(org.kuali.rice.kns.document.MaintenanceDocument,
* java.util.Map)
*/
@Override
public void processAfterNew(MaintenanceDocument document, Map<String, String[]> parameters) {
AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
// set "asset number" and "type code" from URL
setSeparateSourceCapitalAssetParameters(assetGlobal, parameters);
setFinancialDocumentTypeCode(assetGlobal, parameters);
// populate required fields for "Asset Separate" doc
if (getAssetGlobalService().isAssetSeparate(assetGlobal)) {
Asset asset = getAsset(assetGlobal);
AssetOrganization assetOrganization = getAssetOrganization(assetGlobal);
populateAssetSeparateAssetDetails(assetGlobal, asset, assetOrganization);
populateAssetSeparatePaymentDetails(assetGlobal, asset);
populateAssetLocationTabInformation(asset);
AssetGlobalRule.validateAssetTotalCostMatchesPaymentTotalCost(assetGlobal);
if (getAssetGlobalService().isAssetSeparateByPayment(assetGlobal)) {
AssetGlobalRule.validateAssetAlreadySeparated(assetGlobal.getSeparateSourceCapitalAssetNumber());
}
// populate doc header description with the doc type
document.getDocumentHeader().setDocumentDescription(CamsConstants.AssetSeparate.SEPARATE_AN_ASSET_DESCRIPTION);
}
super.processAfterNew(document, parameters);
}
@Override
public void setGenerateDefaultValues(String docTypeName) {
}
@Override
public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) {
super.setupNewFromExisting(document, parameters);
AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
// CSU 6702 BEGIN
String docType = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
ParameterEvaluatorService parameterEvaluatorService = SpringContext.getBean(ParameterEvaluatorService.class);
ParameterEvaluator evaluator = parameterEvaluatorService.getParameterEvaluator(KFSConstants.CoreModuleNamespaces.KFS, KfsParameterConstants.YEAR_END_ACCOUNTING_PERIOD_PARAMETER_NAMES.DETAIL_PARAMETER_TYPE, KfsParameterConstants.YEAR_END_ACCOUNTING_PERIOD_PARAMETER_NAMES.FISCAL_PERIOD_SELECTION_DOCUMENT_TYPES, docType);
if (evaluator.evaluationSucceeds() && isPeriod13(assetGlobal) ) {
Integer closingYear = new Integer(SpringContext.getBean(ParameterService.class).getParameterValueAsString(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, GeneralLedgerConstants.ANNUAL_CLOSING_FISCAL_YEAR_PARM));
String closingDate = getClosingDate(closingYear);
try {
updateAssetGlobalForPeriod13(assetGlobal, closingYear, closingDate);
assetGlobal.refreshNonUpdateableReferences();
} catch (Exception e) {
LOG.error(e);
}
}
// CSU 6702 END
assetGlobal.setLastInventoryDate(getDateTimeService().getCurrentSqlDate());
}
/**
* Get Asset from AssetGlobal
*
* @param assetGlobal
* @return Asset
*/
protected Asset getAsset(AssetGlobal assetGlobal) {
return SpringContext.getBean(BusinessObjectService.class).findBySinglePrimaryKey(Asset.class, assetGlobal.getSeparateSourceCapitalAssetNumber());
}
/**
* Get AssetOrganization from AssetGlobal
*
* @param assetGlobal
* @return AssetOrganization
*/
protected AssetOrganization getAssetOrganization(AssetGlobal assetGlobal) {
return SpringContext.getBean(BusinessObjectService.class).findBySinglePrimaryKey(AssetOrganization.class, assetGlobal.getSeparateSourceCapitalAssetNumber());
}
/**
* Populate Asset Details for Asset Separate document
*
* @param assetGlobal
* @param asset
* @param assetOrganization
*/
private void populateAssetSeparateAssetDetails(AssetGlobal assetGlobal, Asset asset, AssetOrganization assetOrganization) {
assetGlobal.setOrganizationOwnerAccountNumber(asset.getOrganizationOwnerAccountNumber());
assetGlobal.setOrganizationOwnerChartOfAccountsCode(asset.getOrganizationOwnerChartOfAccountsCode());
assetGlobal.setAgencyNumber(asset.getAgencyNumber());
assetGlobal.setAcquisitionTypeCode(asset.getAcquisitionTypeCode());
assetGlobal.setInventoryStatusCode(asset.getInventoryStatusCode());
assetGlobal.setConditionCode(asset.getConditionCode());
assetGlobal.setCapitalAssetDescription(asset.getCapitalAssetDescription());
assetGlobal.setCapitalAssetTypeCode(asset.getCapitalAssetTypeCode());
assetGlobal.setVendorName(asset.getVendorName());
assetGlobal.setManufacturerName(asset.getManufacturerName());
assetGlobal.setManufacturerModelNumber(asset.getManufacturerModelNumber());
if (ObjectUtils.isNotNull(assetOrganization)) {
assetGlobal.setOrganizationText(assetOrganization.getOrganizationText());
}
// added in case of NULL date in DB
if (asset.getLastInventoryDate() == null) {
assetGlobal.setLastInventoryDate(getDateTimeService().getCurrentSqlDate());
}
else {
assetGlobal.setLastInventoryDate(new java.sql.Date(asset.getLastInventoryDate().getTime()));
}
assetGlobal.setCreateDate(asset.getCreateDate());
assetGlobal.setCapitalAssetInServiceDate(asset.getCapitalAssetInServiceDate());
assetGlobal.setLandCountyName(asset.getLandCountyName());
assetGlobal.setLandAcreageSize(asset.getLandAcreageSize());
assetGlobal.setLandParcelNumber(asset.getLandParcelNumber());
// CSU 6702 BEGIN
doPeriod13Changes(assetGlobal);
// CSU 6702 END
assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.ORGANIZATION_OWNER_ACCOUNT);
}
/**
* Populate Asset Payment Details for Asset Separate document. It will do this whether we are separating by asset or payment. If
* it is by asset it picks up all the payments and sets the total amount on the document of that per the asset. If it is by
* payment it picks only the payment out we are interested in and set the document total amount to that payment only.
*
* @param assetGlobal
* @param asset
*/
private void populateAssetSeparatePaymentDetails(AssetGlobal assetGlobal, Asset asset) {
// clear and create temp AssetPaymentDetail list
assetGlobal.getAssetPaymentDetails().clear();
List<AssetPaymentDetail> newAssetPaymentDetailList = assetGlobal.getAssetPaymentDetails();
if (!getAssetGlobalService().isAssetSeparateByPayment(assetGlobal)) {
// Separate by Asset. Pick all payments up
for (AssetPayment assetPayment : asset.getAssetPayments()) {
// create new AssetPaymentDetail
AssetPaymentDetail assetPaymentDetail = new AssetPaymentDetail(assetPayment);
// add assetPaymentDetail to AssetPaymentDetail list
newAssetPaymentDetailList.add(assetPaymentDetail);
}
// Set total amount per asset
assetGlobal.setTotalCostAmount(asset.getTotalCostAmount());
assetGlobal.setSeparateSourceRemainingAmount(asset.getTotalCostAmount());
}
else {
for (AssetPayment assetPayment : asset.getAssetPayments()) {
// Separate by Payment. Pick only the appropriate payment up and then break
if (assetPayment.getPaymentSequenceNumber().equals(assetGlobal.getSeparateSourcePaymentSequenceNumber())) {
// create new AssetPaymentDetail
AssetPaymentDetail assetPaymentDetail = new AssetPaymentDetail(assetPayment);
// add assetPaymentDetail to AssetPaymentDetail list
newAssetPaymentDetailList.add(assetPaymentDetail);
// Set total amount per payment
assetGlobal.setTotalCostAmount(assetPayment.getAccountChargeAmount());
assetGlobal.setSeparateSourceRemainingAmount(assetPayment.getAccountChargeAmount());
break;
}
}
}
assetGlobal.setSeparateSourceTotalAmount(KualiDecimal.ZERO);
// set AssetGlobal payment details with new payment details
assetGlobal.setAssetPaymentDetails(newAssetPaymentDetailList);
}
/**
* Set capital asset number and payment sequence number from URL on the AssetGlobal BO. It only does so if each is available.
*
* @see org.kuali.module.cams.lookup.AssetLookupableHelperServiceImpl#getSeparateUrl(BusinessObject)
* @see org.kuali.module.cams.lookup.AssetPaymentLookupableHelperServiceImpl#getSeparateUrl(BusinessObject)
* @param assetGlobal
* @param parameters
*/
private void setSeparateSourceCapitalAssetParameters(AssetGlobal assetGlobal, Map<String, String[]> parameters) {
String[] separateSourceCapitalAssetNumber = parameters.get(CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET_NUMBER);
if (separateSourceCapitalAssetNumber != null) {
assetGlobal.setSeparateSourceCapitalAssetNumber(Long.parseLong(separateSourceCapitalAssetNumber[0].toString()));
}
String[] separateSourcePaymentSequenceNumber = parameters.get(CamsPropertyConstants.AssetGlobal.SEPERATE_SOURCE_PAYMENT_SEQUENCE_NUMBER);
if (separateSourcePaymentSequenceNumber != null) {
assetGlobal.setSeparateSourcePaymentSequenceNumber(Integer.parseInt(separateSourcePaymentSequenceNumber[0].toString()));
}
}
/**
* Set document type code from URL.
*
* @see org.kuali.module.cams.lookup.AssetLookupableHelperServiceImpl#getSeparateUrl(BusinessObject)
* @param assetGlobal
* @param parameters
*/
private void setFinancialDocumentTypeCode(AssetGlobal assetGlobal, Map<String, String[]> parameters) {
String[] financialDocumentTypeCode = parameters.get(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE);
if (financialDocumentTypeCode != null) {
assetGlobal.setFinancialDocumentTypeCode(financialDocumentTypeCode[0].toString());
}
}
/**
* Hook for quantity and setting asset numbers.
*
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#addNewLineToCollection(java.lang.String)
*/
@Override
public void addNewLineToCollection(String collectionName) {
AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
if (CamsPropertyConstants.AssetGlobal.ASSET_PAYMENT_DETAILS.equalsIgnoreCase(collectionName)) {
handAssetPaymentsCollection(collectionName, assetGlobal);
}
if (CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS.equalsIgnoreCase(collectionName)) {
handleAssetSharedDetailsCollection(collectionName, assetGlobal);
}
int sharedDetailsIndex = assetGlobal.getAssetSharedDetails().size() - 1;
if (sharedDetailsIndex > -1 && (CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + sharedDetailsIndex + "]." + CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS).equalsIgnoreCase(collectionName)) {
handleAssetUniqueCollection(collectionName, assetGlobal);
}
super.addNewLineToCollection(collectionName);
}
/**
* Sets required fields with specific values when an individual unique asset added.
*
* @param collectionName
*/
private void handleAssetUniqueCollection(String collectionName, AssetGlobal assetGlobal) {
AssetGlobalDetail assetGlobalDetail = (AssetGlobalDetail) newCollectionLines.get(collectionName);
if (ObjectUtils.isNotNull(assetGlobalDetail)) {
assetGlobalDetail.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue());
// if not set, populate unique asset fields using original asset data. "Asset Separate" doc (location tab)
if (ObjectUtils.isNotNull(assetGlobal)) {
if (getAssetGlobalService().isAssetSeparate(assetGlobal)) {
if (assetGlobalDetail.getCapitalAssetTypeCode() == null) {
assetGlobalDetail.setCapitalAssetTypeCode(assetGlobal.getCapitalAssetTypeCode());
}
if (assetGlobalDetail.getCapitalAssetDescription() == null) {
assetGlobalDetail.setCapitalAssetDescription(assetGlobal.getCapitalAssetDescription());
}
if (assetGlobalDetail.getManufacturerName() == null) {
assetGlobalDetail.setManufacturerName(assetGlobal.getManufacturerName());
}
if (assetGlobalDetail.getSeparateSourceAmount() == null) {
assetGlobalDetail.setSeparateSourceAmount(KualiDecimal.ZERO);
}
}
}
}
}
/**
* Sets required fields with specific values when multiple unique assets added (i.e. field "Quantity Of Assets To Be Created").
*
* @param collectionName
* @param assetGlobal
*/
private void handleAssetSharedDetailsCollection(String collectionName, AssetGlobal assetGlobal) {
AssetGlobalDetail assetGlobalDetail = (AssetGlobalDetail) newCollectionLines.get(collectionName);
Integer locationQuantity = assetGlobalDetail.getLocationQuantity();
while (locationQuantity != null && locationQuantity > 0) {
AssetGlobalDetail newAssetUnique = new AssetGlobalDetail();
newAssetUnique.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue());
// populate unique asset fields using original asset data. "Asset Separate" doc (location tab)
if (getAssetGlobalService().isAssetSeparate(assetGlobal)) {
newAssetUnique.setCapitalAssetTypeCode(assetGlobal.getCapitalAssetTypeCode());
newAssetUnique.setCapitalAssetDescription(assetGlobal.getCapitalAssetDescription());
newAssetUnique.setManufacturerName(assetGlobal.getManufacturerName());
newAssetUnique.setOrganizationInventoryName(this.getAsset(assetGlobal).getOrganizationInventoryName());
newAssetUnique.setSeparateSourceAmount(KualiDecimal.ZERO);
}
assetGlobalDetail.getAssetGlobalUniqueDetails().add(newAssetUnique);
newAssetUnique.setNewCollectionRecord(true);
locationQuantity--;
}
}
/**
* Sets the default values in some of the fields of the asset payment section
*
* @param collectionName
* @param assetGlobal
*/
private void handAssetPaymentsCollection(String collectionName, AssetGlobal assetGlobal) {
AssetPaymentDetail assetPaymentDetail = (AssetPaymentDetail) newCollectionLines.get(collectionName);
if (assetPaymentDetail != null) {
assetPaymentDetail.setSequenceNumber(assetGlobal.incrementFinancialDocumentLineNumber());
// Set for document number and document type code
if (getAssetGlobalService().existsInGroup(getAssetGlobalService().getNonNewAcquisitionCodeGroup(), assetGlobal.getAcquisitionTypeCode())) {
assetPaymentDetail.setExpenditureFinancialDocumentNumber(getDocumentNumber());
assetPaymentDetail.setExpenditureFinancialDocumentTypeCode(CamsConstants.DocumentTypeName.ASSET_ADD_GLOBAL);
assetPaymentDetail.setExpenditureFinancialSystemOriginationCode(KFSConstants.ORIGIN_CODE_KUALI);
}
// CSU 6702 BEGIN
//year end logic
if (isPeriod13(assetGlobal)) {
assetPaymentDetail.setPostingPeriodCode(assetGlobal.getFinancialDocumentPostingPeriodCode());
assetPaymentDetail.setPostingYear(assetGlobal.getFinancialDocumentPostingYear());
}
// CSU 6702 END
}
}
/**
* We are using a substitute mechanism for asset locking which can lock on assets when rule check passed. Return empty list from
* this method.
*
* @see org.kuali.rice.kns.maintenance.Maintainable#generateMaintenanceLocks()
*/
@Override
public List<MaintenanceLock> generateMaintenanceLocks() {
return new ArrayList<MaintenanceLock>();
}
protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() {
return SpringContext.getBean(CapitalAssetManagementModuleService.class);
}
/**
* @see org.kuali.rice.kns.maintenance.KualiGlobalMaintainableImpl#prepareForSave()
*/
@Override
public void prepareForSave() {
super.prepareForSave();
AssetGlobal assetGlobal = (AssetGlobal) this.getBusinessObject();
//we need to set the posting period and posting year from the value of the drop-down box...
if (StringUtils.isNotBlank(assetGlobal.getUniversityFiscalPeriodName())) {
assetGlobal.setFinancialDocumentPostingPeriodCode(StringUtils.left(assetGlobal.getUniversityFiscalPeriodName(), 2));
assetGlobal.setFinancialDocumentPostingYear(new Integer(StringUtils.right(assetGlobal.getUniversityFiscalPeriodName(), 4)));
}
List<AssetGlobalDetail> assetSharedDetails = assetGlobal.getAssetSharedDetails();
List<AssetGlobalDetail> newDetails = new ArrayList<AssetGlobalDetail>();
AssetGlobalDetail newAssetGlobalDetail = null;
if (!assetSharedDetails.isEmpty() && !assetSharedDetails.get(0).getAssetGlobalUniqueDetails().isEmpty()) {
for (AssetGlobalDetail locationDetail : assetSharedDetails) {
List<AssetGlobalDetail> assetGlobalUniqueDetails = locationDetail.getAssetGlobalUniqueDetails();
for (AssetGlobalDetail detail : assetGlobalUniqueDetails) {
// read from location and set it to detail
if (ObjectUtils.isNotNull(locationDetail.getCampusCode())) {
detail.setCampusCode(locationDetail.getCampusCode().toUpperCase());
}
else {
detail.setCampusCode(locationDetail.getCampusCode());
}
if (ObjectUtils.isNotNull(locationDetail.getBuildingCode())) {
detail.setBuildingCode(locationDetail.getBuildingCode().toUpperCase());
}
else {
detail.setBuildingCode(locationDetail.getBuildingCode());
}
detail.setBuildingRoomNumber(locationDetail.getBuildingRoomNumber());
detail.setBuildingSubRoomNumber(locationDetail.getBuildingSubRoomNumber());
detail.setOffCampusName(locationDetail.getOffCampusName());
detail.setOffCampusAddress(locationDetail.getOffCampusAddress());
detail.setOffCampusCityName(locationDetail.getOffCampusCityName());
detail.setOffCampusStateCode(locationDetail.getOffCampusStateCode());
detail.setOffCampusCountryCode(locationDetail.getOffCampusCountryCode());
detail.setOffCampusZipCode(locationDetail.getOffCampusZipCode());
newDetails.add(detail);
}
}
}
if (assetGlobal.getCapitalAssetTypeCode() != null) {
assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.CAPITAL_ASSET_TYPE);
AssetType capitalAssetType = assetGlobal.getCapitalAssetType();
if (ObjectUtils.isNotNull(capitalAssetType)) {
if (capitalAssetType.getDepreciableLifeLimit() != null && capitalAssetType.getDepreciableLifeLimit().intValue() != 0) {
assetGlobal.setCapitalAssetInServiceDate(assetGlobal.getCreateDate() == null ? getDateTimeService().getCurrentSqlDate() : assetGlobal.getCreateDate());
}
else {
assetGlobal.setCapitalAssetInServiceDate(null);
}
computeDepreciationDate(assetGlobal);
// CSU 6702 BEGIN
doPeriod13Changes(assetGlobal);
// CSU 6702 END
}
}
assetGlobal.getAssetGlobalDetails().clear();
assetGlobal.getAssetGlobalDetails().addAll(newDetails);
}
/**
* computes depreciation date
*
* @param assetGlobal
*/
private void computeDepreciationDate(AssetGlobal assetGlobal) {
List<AssetPaymentDetail> assetPaymentDetails = assetGlobal.getAssetPaymentDetails();
if (assetPaymentDetails != null && !assetPaymentDetails.isEmpty()) {
LOG.debug("Compute depreciation date based on asset type, depreciation convention and in-service date");
AssetPaymentDetail firstAssetPaymentDetail = assetPaymentDetails.get(0);
ObjectCode objectCode = SpringContext.getBean(ObjectCodeService.class).getByPrimaryId(firstAssetPaymentDetail.getPostingYear(), firstAssetPaymentDetail.getChartOfAccountsCode(), firstAssetPaymentDetail.getFinancialObjectCode());
if (ObjectUtils.isNotNull(objectCode)) {
Map<String, String> primaryKeys = new HashMap<String, String>();
primaryKeys.put(CamsPropertyConstants.AssetDepreciationConvention.FINANCIAL_OBJECT_SUB_TYPE_CODE, objectCode.getFinancialObjectSubTypeCode());
AssetDepreciationConvention depreciationConvention = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(AssetDepreciationConvention.class, primaryKeys);
Date depreciationDate = SpringContext.getBean(AssetDateService.class).computeDepreciationDate(assetGlobal.getCapitalAssetType(), depreciationConvention, assetGlobal.getCapitalAssetInServiceDate());
assetGlobal.setCapitalAssetDepreciationDate(depreciationDate);
}
}
}
/**
* @see org.kuali.rice.kns.maintenance.KualiGlobalMaintainableImpl#processAfterRetrieve()
*/
@Override
public void processAfterRetrieve() {
super.processAfterRetrieve();
AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
assetGlobal.refresh();
assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET);
if (ObjectUtils.isNotNull(assetGlobal.getSeparateSourceCapitalAsset())) {
assetGlobal.setLastInventoryDate(new java.sql.Date(assetGlobal.getSeparateSourceCapitalAsset().getLastInventoryDate().getTime()));
// CSU 6702 BEGIN
//year end logic
doPeriod13Changes(assetGlobal);
// CSU 6702 END
}
else {
assetGlobal.setLastInventoryDate(getDateTimeService().getCurrentSqlDate());
// CSU 6702 BEGIN
doPeriod13Changes(assetGlobal);
// CSU 6702 END
}
List<AssetGlobalDetail> assetGlobalDetails = assetGlobal.getAssetGlobalDetails();
AssetGlobalDetail currLocationDetail = null;
HashMap<String, AssetGlobalDetail> locationMap = new HashMap<String, AssetGlobalDetail>();
AssetGlobalDetail copyValue = null;
for (AssetGlobalDetail detail : assetGlobalDetails) {
copyValue = (AssetGlobalDetail) ObjectUtils.deepCopy(detail);
copyValue.getAssetGlobalUniqueDetails().clear();
String key = generateLocationKey(copyValue);
if ((currLocationDetail = locationMap.get(key)) == null) {
currLocationDetail = copyValue;
locationMap.put(key, currLocationDetail);
}
currLocationDetail.getAssetGlobalUniqueDetails().add(copyValue);
currLocationDetail.setLocationQuantity(currLocationDetail.getAssetGlobalUniqueDetails().size());
}
assetGlobal.getAssetSharedDetails().clear();
assetGlobal.getAssetSharedDetails().addAll(locationMap.values());
// When document starts routing, FO won't allow to change asset total amount which is a derived value from Asset payments
// and the quantity of assets. To compare asset total amount , we need to calculate and save the value before FO made
// changes. No handle to the workflow document and see if it starts routing. Otherwise, we can add if condition here.
setAssetTotalAmountFromPersistence(assetGlobal);
}
private void setAssetTotalAmountFromPersistence(AssetGlobal assetGlobal) {
KualiDecimal minAssetTotalAmount = getAssetGlobalService().totalPaymentByAsset(assetGlobal, false);
KualiDecimal maxAssetTotalAmount = getAssetGlobalService().totalPaymentByAsset(assetGlobal, true);
if (minAssetTotalAmount.isGreaterThan(maxAssetTotalAmount)) {
// swap min and max
KualiDecimal totalPayment = minAssetTotalAmount;
minAssetTotalAmount = maxAssetTotalAmount;
maxAssetTotalAmount = totalPayment;
}
assetGlobal.setMinAssetTotalAmount(minAssetTotalAmount);
assetGlobal.setMaxAssetTotalAmount(maxAssetTotalAmount);
}
/**
* Generates a unique using location fields to keep track of user changes
*
* @param location Location
* @return Key String
*/
private String generateLocationKey(AssetGlobalDetail location) {
StringBuilder builder = new StringBuilder();
builder.append(location.getCampusCode() == null ? "" : location.getCampusCode().trim().toLowerCase());
builder.append(location.getBuildingCode() == null ? "" : location.getBuildingCode().trim().toLowerCase());
builder.append(location.getBuildingRoomNumber() == null ? "" : location.getBuildingRoomNumber().trim().toLowerCase());
builder.append(location.getBuildingSubRoomNumber() == null ? "" : location.getBuildingSubRoomNumber().trim().toLowerCase());
builder.append(location.getOffCampusName() == null ? "" : location.getOffCampusName().trim().toLowerCase());
builder.append(location.getOffCampusAddress() == null ? "" : location.getOffCampusAddress().trim().toLowerCase());
builder.append(location.getOffCampusCityName() == null ? "" : location.getOffCampusCityName().trim().toLowerCase());
builder.append(location.getOffCampusStateCode() == null ? "" : location.getOffCampusStateCode().trim().toLowerCase());
builder.append(location.getOffCampusZipCode() == null ? "" : location.getOffCampusZipCode().trim().toLowerCase());
builder.append(location.getOffCampusCountryCode() == null ? "" : location.getOffCampusCountryCode().trim().toLowerCase());
return builder.toString();
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterPost(org.kuali.rice.kns.document.MaintenanceDocument,
* java.util.Map)
*/
@Override
public void processAfterPost(MaintenanceDocument document, Map<String, String[]> parameters) {
super.processAfterPost(document, parameters);
// adjust the quantity
AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
List<AssetGlobalDetail> sharedDetailsList = assetGlobal.getAssetSharedDetails();
// each shared detail is a group of new assets to be created.
// so to equally split the source amount into all new assets (all groups),
// we need to get the total of ALL location quantities from each shared detail group
int locationQtyTotal = 0;
if (!sharedDetailsList.isEmpty()) {
for (AssetGlobalDetail sharedDetail : sharedDetailsList) {
sharedDetail.setLocationQuantity(sharedDetail.getAssetGlobalUniqueDetails().size());
locationQtyTotal += sharedDetail.getLocationQuantity();
}
}
// button actions for Asset Separate document
if (getAssetGlobalService().isAssetSeparate(assetGlobal) && sharedDetailsList.size() >= 1) {
String[] customAction = parameters.get(KRADConstants.CUSTOM_ACTION);
// calculate equal source total amounts and set separate source amount fields
if (customAction != null && CamsConstants.AssetSeparate.CALCULATE_EQUAL_SOURCE_AMOUNTS_BUTTON.equals(customAction[0])) {
// add source asset to the current location quantity
KualiDecimal[] equalSourceAmountsArray = KualiDecimalUtils.allocateByQuantity(assetGlobal.getTotalCostAmount(), locationQtyTotal + 1);
setEqualSeparateSourceAmounts(equalSourceAmountsArray, assetGlobal);
recalculateTotalAmount(assetGlobal);
}
// calculate source asset remaining amount
if (customAction != null && (CamsConstants.AssetSeparate.CALCULATE_SEPARATE_SOURCE_REMAINING_AMOUNT_BUTTON.equals(customAction[0]))) {
// Don't do anything because we are anyway recalculating always below
}
// Do recalculate every time even if button (CamsConstants.CALCULATE_SEPARATE_SOURCE_REMAINING_AMOUNT_BUTTON) wasn't
// pressed. We do that so that it also happens on add / delete lines.
recalculateTotalAmount(assetGlobal);
}
}
/**
* Recalculate amounts in the Recalculate Total Amount Tab
*
* @param assetGlobal
*/
protected void recalculateTotalAmount(AssetGlobal assetGlobal) {
// set Less Additions
assetGlobal.setSeparateSourceTotalAmount(getAssetGlobalService().getUniqueAssetsTotalAmount(assetGlobal));
// set Remaining Total Amount
assetGlobal.setSeparateSourceRemainingAmount(assetGlobal.getTotalCostAmount().subtract(getAssetGlobalService().getUniqueAssetsTotalAmount(assetGlobal)));
}
/**
* Separates the current asset amount equally into new unique assets.
*
* @param kualiDecimalArray
* @param assetGlobal
*/
public void setEqualSeparateSourceAmounts(KualiDecimal[] equalSourceAmountsArray, AssetGlobal assetGlobal) {
int i = 0;
for (AssetGlobalDetail assetSharedDetail : assetGlobal.getAssetSharedDetails()) {
for (AssetGlobalDetail assetGlobalUniqueDetail : assetSharedDetail.getAssetGlobalUniqueDetails()) {
assetGlobalUniqueDetail.setSeparateSourceAmount(equalSourceAmountsArray[i]);
i++;
}
}
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.kns.bo.DocumentHeader)
*/
@Override
public void doRouteStatusChange(DocumentHeader documentHeader) {
super.doRouteStatusChange(documentHeader);
AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
List<GeneralLedgerPendingEntry> generalLedgerPendingEntries = assetGlobal.getGeneralLedgerPendingEntries();
new AssetGlobalGeneralLedgerPendingEntrySource((FinancialSystemDocumentHeader) documentHeader).doRouteStatusChange(generalLedgerPendingEntries);
WorkflowDocument workflowDoc = documentHeader.getWorkflowDocument();
// force pretagDetail active indicators back to true
if (workflowDoc.isCanceled()) {
if (ObjectUtils.isNotNull(assetGlobal)) {
List<AssetGlobalDetail> assetGlobalDetailsList = assetGlobal.getAssetGlobalDetails();
for (AssetGlobalDetail assetGlobalDetails : assetGlobalDetailsList) {
SpringContext.getBean(CapitalAssetBuilderModuleService.class).reactivatePretagDetails(assetGlobalDetails.getCampusTagNumber());
}
}
}
// release lock for separate source asset...We don't include stateIsFinal since document always go to 'processed' first.
AssetGlobalService assetGlobalService = SpringContext.getBean(AssetGlobalService.class);
if (assetGlobalService.isAssetSeparate(assetGlobal) && (workflowDoc.isCanceled() || workflowDoc.isDisapproved() || workflowDoc.isProcessed())) {
this.getCapitalAssetManagementModuleService().deleteAssetLocks(getDocumentNumber(), null);
}
// notify CAB of document status change
if (((AssetGlobal) getBusinessObject()).isCapitalAssetBuilderOriginIndicator()) {
SpringContext.getBean(CapitalAssetBuilderModuleService.class).notifyRouteStatusChange(documentHeader);
}
}
/**
* @see org.kuali.rice.kns.maintenance.KualiGlobalMaintainableImpl#getPrimaryEditedBusinessObjectClass()
*/
@Override
public Class<? extends PersistableBusinessObject> getPrimaryEditedBusinessObjectClass() {
return Asset.class;
}
/**
* Returns the AssetGlobalService from context
*
* @return AssetGlobalService
*/
private AssetGlobalService getAssetGlobalService() {
return SpringContext.getBean(AssetGlobalService.class);
}
/**
* populates the asset location information (add new section)
*
* @param asset
*/
private void populateAssetLocationTabInformation(Asset asset) {
AssetGlobalDetail assetSharedDetail = (AssetGlobalDetail) this.getNewCollectionLine(CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS);
assetSharedDetail.setCampusCode(asset.getCampusCode());
assetSharedDetail.setBuildingCode(asset.getBuildingCode());
assetSharedDetail.setBuildingRoomNumber(asset.getBuildingRoomNumber());
}
// CSU 6702 BEGIN
/**
* Checks for Accounting Period 13
* @param assetGlobal
* @return true if the accountingPeriod in assetGlobal is 13.
* TODO Remove hardcoding
*/
private boolean isPeriod13(AssetGlobal assetGlobal) {
if (ObjectUtils.isNull(assetGlobal.getAccountingPeriod())) {
return false;
}
return "13".equals(assetGlobal.getAccountingPeriod().getUniversityFiscalPeriodCode());
}
/**
* Return the closing date as mm/dd/yyyy
* @param closingYear
* @return the closing date as mm/dd/yyyy
*/
private String getClosingDate(Integer closingYear) {
return getAssetGlobalService().getFiscalYearEndDayAndMonth() + closingYear.toString();
}
/**
* Return the calendar Date for the closing year
* @param closingYear
* @return 01/01/[closing year]
* TODO Remove hardcoding
*/
private String getClosingCalendarDate(Integer closingYear) {
return "01/01/" + closingYear.toString();
}
/**
* Convenience method to reduce clutter
* @return {@link DateTimeService}
*/
private DateTimeService getDateTimeService() {
return SpringContext.getBean(DateTimeService.class);
}
/**
* Perform changes to assetGlobal on period 13.
* @param assetGlobal
*/
private void doPeriod13Changes(AssetGlobal assetGlobal) {
if (isPeriod13(assetGlobal)) {
Integer closingYear = new Integer(SpringContext.getBean(ParameterService.class).getParameterValueAsString(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, GeneralLedgerConstants.ANNUAL_CLOSING_FISCAL_YEAR_PARM));
String closingDate = getClosingDate(closingYear);
try {
updateAssetGlobalForPeriod13(assetGlobal, closingYear, closingDate);
} catch (Exception e) {
LOG.error(e);
}
}
}
/**
* Update assetGlobal fields for period 13
* @param assetGlobal
* @param closingYear
* @param closingDate
* @throws ParseException
*/
private void updateAssetGlobalForPeriod13(AssetGlobal assetGlobal, Integer closingYear, String closingDate) throws ParseException {
assetGlobal.setCreateDate(getDateTimeService().getCurrentSqlDate());
assetGlobal.setCapitalAssetInServiceDate(getDateTimeService().convertToSqlDate(closingDate));
assetGlobal.setCreateDate(getDateTimeService().convertToSqlDate(closingDate));
assetGlobal.setCapitalAssetDepreciationDate(getDateTimeService().convertToSqlDate(getClosingCalendarDate(closingYear)));
assetGlobal.setLastInventoryDate(getDateTimeService().getCurrentSqlDate());
}
// CSU 6702 END
/**
* @see org.kuali.kfs.sys.document.FinancialSystemMaintainable#populateChartOfAccountsCodeFields()
*
* Special treatment is needed to populate the chart code from the account number field in AssetPaymentDetails,
* as these fields aren't PKs of BO class in the collection.
*/
@Override
protected void populateChartOfAccountsCodeFields() {
super.populateChartOfAccountsCodeFields();
AccountService acctService = SpringContext.getBean(AccountService.class);
PersistableBusinessObject newAccount = getNewCollectionLine(CamsPropertyConstants.AssetGlobal.ASSET_PAYMENT_DETAILS);
String accountNumber = (String)ObjectUtils.getPropertyValue(newAccount, KFSPropertyConstants.ACCOUNT_NUMBER);
String coaCode = null;
Account account = acctService.getUniqueAccountForAccountNumber(accountNumber);
if (ObjectUtils.isNotNull(account)) {
coaCode = account.getChartOfAccountsCode();
}
try {
ObjectUtils.setObjectProperty(newAccount, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, coaCode);
}
catch (Exception e) {
LOG.error("Error in setting property value for " + KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE);
}
}
}