/* * 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.cab.document.service.impl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.kuali.kfs.coa.service.ObjectTypeService; import org.kuali.kfs.fp.businessobject.CapitalAccountingLines; import org.kuali.kfs.fp.businessobject.CapitalAssetAccountsGroupDetails; import org.kuali.kfs.fp.businessobject.CapitalAssetInformation; import org.kuali.kfs.fp.businessobject.CapitalAssetInformationDetail; import org.kuali.kfs.fp.document.dataaccess.CapitalAssetInformationDao; import org.kuali.kfs.gl.GeneralLedgerConstants; import org.kuali.kfs.module.cab.CabConstants; import org.kuali.kfs.module.cab.CabPropertyConstants; import org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntry; import org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntryAsset; import org.kuali.kfs.module.cab.document.service.GlLineService; import org.kuali.kfs.module.cam.CamsConstants; import org.kuali.kfs.module.cam.CamsConstants.DocumentTypeName; import org.kuali.kfs.module.cam.CamsPropertyConstants; import org.kuali.kfs.module.cam.businessobject.Asset; import org.kuali.kfs.module.cam.businessobject.AssetGlobal; import org.kuali.kfs.module.cam.businessobject.AssetGlobalDetail; import org.kuali.kfs.module.cam.businessobject.AssetPaymentAssetDetail; import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail; import org.kuali.kfs.module.cam.businessobject.defaultvalue.NextAssetNumberFinder; import org.kuali.kfs.module.cam.document.AssetPaymentDocument; import org.kuali.kfs.module.cam.document.service.AssetGlobalService; import org.kuali.kfs.module.cam.util.ObjectValueUtils; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader; import org.kuali.kfs.sys.businessobject.SourceAccountingLine; import org.kuali.kfs.sys.service.NonTransactional; import org.kuali.kfs.sys.service.impl.KfsParameterConstants; 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.exception.WorkflowException; import org.kuali.rice.kns.document.MaintenanceDocument; import org.kuali.rice.krad.bo.DocumentHeader; import org.kuali.rice.krad.document.Document; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.DocumentHeaderService; import org.kuali.rice.krad.service.DocumentService; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.ObjectUtils; import org.springframework.transaction.annotation.Transactional; public class GlLineServiceImpl implements GlLineService { private static final String CAB_DESC_PREFIX = "CAB created for FP "; protected BusinessObjectService businessObjectService; protected AssetGlobalService assetGlobalService; protected ObjectTypeService objectTypeService; protected DocumentService documentService; protected ParameterService parameterService; protected ParameterEvaluatorService parameterEvaluatorService; protected DocumentHeaderService documentHeaderService; protected CapitalAssetInformationDao capitalAssetInformationDao; /** * @see org.kuali.kfs.module.cab.document.service.GlLineService#createAssetGlobalDocument(java.util.List, * org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntry) */ @Override @NonTransactional public Document createAssetGlobalDocument(GeneralLedgerEntry primary, Integer capitalAssetLineNumber) throws WorkflowException { // initiate a new document MaintenanceDocument document = (MaintenanceDocument) documentService.getNewDocument(DocumentTypeName.ASSET_ADD_GLOBAL); // create asset global AssetGlobal assetGlobal = createAssetGlobal(primary, document); assetGlobal.setCapitalAssetBuilderOriginIndicator(true); assetGlobal.setAcquisitionTypeCode(assetGlobalService.getNewAcquisitionTypeCode()); updatePreTagInformation(primary, document, assetGlobal, capitalAssetLineNumber); assetGlobal.getAssetPaymentDetails().addAll(createAssetPaymentDetails(primary, document, 0, capitalAssetLineNumber)); // save the document document.getNewMaintainableObject().setMaintenanceAction(KRADConstants.MAINTENANCE_NEW_ACTION); document.getDocumentHeader().setDocumentDescription(CAB_DESC_PREFIX + primary.getDocumentNumber()); document.getNewMaintainableObject().setBusinessObject(assetGlobal); document.getNewMaintainableObject().setBoClass(assetGlobal.getClass()); documentService.saveDocument(document); //mark the capital asset as processed.. markCapitalAssetProcessed(primary, capitalAssetLineNumber); deactivateGLEntries(primary, document, capitalAssetLineNumber); return document; } protected void markCapitalAssetProcessed(GeneralLedgerEntry primary, Integer capitalAssetLineNumber) { CapitalAssetInformation capitalAssetInformation = findCapitalAssetInformation(primary.getDocumentNumber(), capitalAssetLineNumber); //if it is create asset... if (ObjectUtils.isNotNull(capitalAssetInformation)) { capitalAssetInformation.setCapitalAssetProcessedIndicator(true); businessObjectService.save(capitalAssetInformation); } } protected void deactivateGLEntries(GeneralLedgerEntry entry, Document document, Integer capitalAssetLineNumber) { //now deactivate the gl line.. CapitalAssetInformation capitalAssetInformation = findCapitalAssetInformation(entry.getDocumentNumber(), capitalAssetLineNumber); if (ObjectUtils.isNotNull(capitalAssetInformation)) { List<CapitalAssetAccountsGroupDetails> groupAccountingLines = capitalAssetInformation.getCapitalAssetAccountsGroupDetails(); Collection<GeneralLedgerEntry> documentGlEntries = findAllGeneralLedgerEntry(entry.getDocumentNumber()); for (CapitalAssetAccountsGroupDetails accountingLine : groupAccountingLines) { //find the matching GL entry for this accounting line. Collection<GeneralLedgerEntry> glEntries = findMatchingGeneralLedgerEntries(documentGlEntries, accountingLine); for (GeneralLedgerEntry glEntry : glEntries) { KualiDecimal lineAmount = accountingLine.getAmount(); //update submitted amount on the gl entry and save the results. createGeneralLedgerEntryAsset(glEntry, document, capitalAssetLineNumber); updateTransactionSumbitGlEntryAmount(glEntry, lineAmount); } } } } /** * This method reads the pre-tag information and creates objects for asset global document * * @param entry GL Line * @param document Asset Global Maintenance Document * @param assetGlobal Asset Global Object */ protected void updatePreTagInformation(GeneralLedgerEntry entry, MaintenanceDocument document, AssetGlobal assetGlobal, Integer capitalAssetLineNumber) { CapitalAssetInformation capitalAssetInformation = findCapitalAssetInformation(entry.getDocumentNumber(), capitalAssetLineNumber); //if it is create asset... if (ObjectUtils.isNotNull(capitalAssetInformation)) { if (KFSConstants.CapitalAssets.CAPITAL_ASSET_CREATE_ACTION_INDICATOR.equals(capitalAssetInformation.getCapitalAssetActionIndicator())) { List<CapitalAssetInformationDetail> capitalAssetInformationDetails = capitalAssetInformation.getCapitalAssetInformationDetails(); for (CapitalAssetInformationDetail capitalAssetInformationDetail : capitalAssetInformationDetails) { // This is not added to constructor in CAMS to provide module isolation from CAMS AssetGlobalDetail assetGlobalDetail = new AssetGlobalDetail(); assetGlobalDetail.setDocumentNumber(document.getDocumentNumber()); assetGlobalDetail.setCampusCode(capitalAssetInformationDetail.getCampusCode()); assetGlobalDetail.setBuildingCode(capitalAssetInformationDetail.getBuildingCode()); assetGlobalDetail.setBuildingRoomNumber(capitalAssetInformationDetail.getBuildingRoomNumber()); assetGlobalDetail.setBuildingSubRoomNumber(capitalAssetInformationDetail.getBuildingSubRoomNumber()); assetGlobalDetail.setSerialNumber(capitalAssetInformationDetail.getCapitalAssetSerialNumber()); assetGlobalDetail.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue()); assetGlobalDetail.setCampusTagNumber(capitalAssetInformationDetail.getCapitalAssetTagNumber()); AssetGlobalDetail uniqueAsset = new AssetGlobalDetail(); ObjectValueUtils.copySimpleProperties(assetGlobalDetail, uniqueAsset); assetGlobalDetail.getAssetGlobalUniqueDetails().add(uniqueAsset); assetGlobal.getAssetSharedDetails().add(assetGlobalDetail); } assetGlobal.setVendorName(capitalAssetInformation.getVendorName()); assetGlobal.setInventoryStatusCode(CamsConstants.InventoryStatusCode.CAPITAL_ASSET_ACTIVE_IDENTIFIABLE); assetGlobal.setCapitalAssetTypeCode(capitalAssetInformation.getCapitalAssetTypeCode()); assetGlobal.setManufacturerName(capitalAssetInformation.getCapitalAssetManufacturerName()); assetGlobal.setManufacturerModelNumber(capitalAssetInformation.getCapitalAssetManufacturerModelNumber()); assetGlobal.setCapitalAssetDescription(capitalAssetInformation.getCapitalAssetDescription()); } } } /** * @see org.kuali.kfs.module.cab.document.service.GlLineService#findCapitalAssetInformation(org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntry) */ @Override @NonTransactional public CapitalAssetInformation findCapitalAssetInformation(String documentNumber, Integer capitalAssetLineNumber) { Map<String, String> primaryKeys = new HashMap<String, String>(2); primaryKeys.put(CabPropertyConstants.CapitalAssetInformation.DOCUMENT_NUMBER, documentNumber); primaryKeys.put(CabPropertyConstants.CapitalAssetInformation.ASSET_LINE_NUMBER, capitalAssetLineNumber.toString()); CapitalAssetInformation assetInformation = businessObjectService.findByPrimaryKey(CapitalAssetInformation.class, primaryKeys); return assetInformation; } @Override @NonTransactional public List<CapitalAssetInformation> findAllCapitalAssetInformation(String documentNumber) { Map<String, String> primaryKeys = new HashMap<String, String>(1); primaryKeys.put(CabPropertyConstants.CapitalAssetInformation.DOCUMENT_NUMBER, documentNumber); List<CapitalAssetInformation> assetInformation = (List<CapitalAssetInformation>) businessObjectService.findMatchingOrderBy(CapitalAssetInformation.class, primaryKeys, CabPropertyConstants.CapitalAssetInformation.ACTION_INDICATOR, true); return assetInformation; } /** * @see org.kuali.kfs.module.cab.document.service.GlLineService#findCapitalAssetInformationForGLLine(org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntry) */ @Override @NonTransactional public List<CapitalAssetInformation> findCapitalAssetInformationForGLLine(GeneralLedgerEntry entry) { Map<String, String> primaryKeys = new HashMap<String, String>(); primaryKeys.put(CabPropertyConstants.CapitalAssetInformation.DOCUMENT_NUMBER, entry.getDocumentNumber()); List<CapitalAssetInformation> assetInformation = (List<CapitalAssetInformation>) businessObjectService.findMatchingOrderBy(CapitalAssetInformation.class, primaryKeys, CabPropertyConstants.CapitalAssetInformation.ACTION_INDICATOR, true); List<CapitalAssetInformation> matchingAssets = new ArrayList<CapitalAssetInformation>(); for (CapitalAssetInformation capitalAsset : assetInformation) { addToCapitalAssets(matchingAssets, capitalAsset, entry); } return matchingAssets; } /** * Compares the gl line to the group accounting lines in each capital asset and * when finds a match, adds the capital asset to the list of matching assets * @param matchingAssets * @param capitalAsset * @param entry * @param capitalAssetLineType */ protected void addToCapitalAssets(List<CapitalAssetInformation> matchingAssets, CapitalAssetInformation capitalAsset, GeneralLedgerEntry entry) { List<CapitalAssetAccountsGroupDetails> groupAccountLines = capitalAsset.getCapitalAssetAccountsGroupDetails(); for (CapitalAssetAccountsGroupDetails groupAccountLine : groupAccountLines) { // Unfortunately, when no organization reference id is input, the General Ledger Entry's organizationReferenceId is // set to "" while the groupAccountLine's organizationReferenceId is set to null. These are equivalent for the // present concerns. boolean isOrganizationReferenceIdEqual; if (StringUtils.equals(groupAccountLine.getOrganizationReferenceId(), entry.getOrganizationReferenceId())){ isOrganizationReferenceIdEqual = true; } else if (StringUtils.isBlank(groupAccountLine.getOrganizationReferenceId()) && StringUtils.isBlank(entry.getOrganizationReferenceId())){ isOrganizationReferenceIdEqual = true; } else { isOrganizationReferenceIdEqual = false; } if (isOrganizationReferenceIdEqual && StringUtils.equals(groupAccountLine.getDocumentNumber(), entry.getDocumentNumber()) && StringUtils.equals(groupAccountLine.getChartOfAccountsCode(), entry.getChartOfAccountsCode()) && StringUtils.equals(groupAccountLine.getAccountNumber(), entry.getAccountNumber()) && StringUtils.equals(groupAccountLine.getFinancialObjectCode(), entry.getFinancialObjectCode()) ){ matchingAssets.add(capitalAsset); break; } } } @Override @NonTransactional public long findUnprocessedCapitalAssetInformation( String documentNumber ) { Map<String, String> fieldValues = new HashMap<String, String>(2); fieldValues.put(CabPropertyConstants.CapitalAssetInformation.DOCUMENT_NUMBER, documentNumber); fieldValues.put(CabPropertyConstants.CapitalAssetInformation.ASSET_PROCESSED_IND, KFSConstants.CapitalAssets.CAPITAL_ASSET_PROCESSED_IND); return businessObjectService.countMatching(CapitalAssetInformation.class, fieldValues); } @Override @NonTransactional public Collection<GeneralLedgerEntry> findMatchingGeneralLedgerEntries( Collection<GeneralLedgerEntry> allGLEntries, CapitalAssetAccountsGroupDetails accountingDetails ) { Collection<GeneralLedgerEntry> matchingGLEntries = new ArrayList<GeneralLedgerEntry>(); for ( GeneralLedgerEntry entry : allGLEntries ) { if ( doesGeneralLedgerEntryMatchAssetAccountingDetails(entry, accountingDetails) ) { matchingGLEntries.add(entry); } } return matchingGLEntries; } protected boolean doesGeneralLedgerEntryMatchAssetAccountingDetails( GeneralLedgerEntry entry, CapitalAssetAccountsGroupDetails accountingDetails ) { // this method will short-circuit and return false as soon as possible // sanity check - the arguments should already have the same document number if ( !StringUtils.equals( entry.getDocumentNumber(), accountingDetails.getDocumentNumber() ) ) { return false; } // required attributes - easy to compare if ( !StringUtils.equals( entry.getAccountNumber(), accountingDetails.getAccountNumber() ) ) { return false; } if ( !StringUtils.equals( entry.getFinancialObjectCode(), accountingDetails.getFinancialObjectCode() ) ) { return false; } if ( !StringUtils.equals( entry.getChartOfAccountsCode(), accountingDetails.getChartOfAccountsCode() ) ) { return false; } // account for blank equaling null if ( !StringUtils.equals( entry.getOrganizationReferenceId(), accountingDetails.getOrganizationReferenceId() ) ) { if ( StringUtils.isBlank( entry.getOrganizationReferenceId() ) && StringUtils.isBlank( accountingDetails.getOrganizationReferenceId() ) ) { // this is a match, keep going } else { return false; } } // optional attributes - need to account for blank being equivalent to dashes // it's always dashes on the CAB GL Entry table - but could be blank on the accounting details if ( !StringUtils.equals( entry.getSubAccountNumber(), accountingDetails.getSubAccountNumber() ) ) { if ( StringUtils.equals( entry.getSubAccountNumber(), KFSConstants.getDashSubAccountNumber() ) && StringUtils.isBlank( accountingDetails.getSubAccountNumber() ) ) { // this is a match, keep going } else { return false; } } if ( !StringUtils.equals( entry.getFinancialSubObjectCode(), accountingDetails.getFinancialSubObjectCode() ) ) { if ( StringUtils.equals( entry.getFinancialSubObjectCode(), KFSConstants.getDashFinancialSubObjectCode() ) && StringUtils.isBlank( accountingDetails.getFinancialSubObjectCode() ) ) { // this is a match, keep going } else { return false; } } if ( !StringUtils.equals( entry.getProjectCode(), accountingDetails.getProjectCode() ) ) { if ( StringUtils.equals( entry.getProjectCode(), KFSConstants.getDashProjectCode() ) && StringUtils.isBlank( accountingDetails.getProjectCode() ) ) { // this is a match, keep going } else { return false; } } return true; } @Override @NonTransactional public Collection<GeneralLedgerEntry> findAllGeneralLedgerEntry(String documentNumber) { Map<String, String> fieldValues = new HashMap<String, String>(1); fieldValues.put(CabPropertyConstants.GeneralLedgerEntry.DOCUMENT_NUMBER, documentNumber); return businessObjectService.findMatching(GeneralLedgerEntry.class, fieldValues); } protected void createGeneralLedgerEntryAsset(GeneralLedgerEntry entry, Document document, Integer capitalAssetLineNumber) { // KFSMI-9645 : check if the document is already referenced to prevent an OJB locking error for ( GeneralLedgerEntryAsset glEntryAsset : entry.getGeneralLedgerEntryAssets() ) { if ( glEntryAsset.getCapitalAssetManagementDocumentNumber().equals(document.getDocumentNumber() ) && glEntryAsset.getCapitalAssetBuilderLineNumber().equals(capitalAssetLineNumber ) ) { // an object with this key already exists, abort and don't attempt to add another return; } } // If we get here, add a child record with the document number GeneralLedgerEntryAsset entryAsset = new GeneralLedgerEntryAsset(); entryAsset.setGeneralLedgerAccountIdentifier(entry.getGeneralLedgerAccountIdentifier()); entryAsset.setCapitalAssetBuilderLineNumber(capitalAssetLineNumber); entryAsset.setCapitalAssetManagementDocumentNumber(document.getDocumentNumber()); entry.getGeneralLedgerEntryAssets().add(entryAsset); } /** * Creates asset global * * @param entry GeneralLedgerEntry * @param maintDoc MaintenanceDocument * @return AssetGlobal */ protected AssetGlobal createAssetGlobal(GeneralLedgerEntry entry, MaintenanceDocument maintDoc) { AssetGlobal assetGlobal = new AssetGlobal(); assetGlobal.setOrganizationOwnerChartOfAccountsCode(entry.getChartOfAccountsCode()); assetGlobal.setOrganizationOwnerAccountNumber(entry.getAccountNumber()); assetGlobal.setDocumentNumber(maintDoc.getDocumentNumber()); assetGlobal.setConditionCode(CamsConstants.Asset.CONDITION_CODE_E); // CSU 6702 BEGIN //year end changes String docType = DocumentTypeName.ASSET_ADD_GLOBAL; 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()) { Integer closingYear = new Integer(parameterService.getParameterValueAsString(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, GeneralLedgerConstants.ANNUAL_CLOSING_FISCAL_YEAR_PARM)); if (entry.getUniversityFiscalYear().equals(closingYear + 1)) { //default asset global year end accounting period drop down to current period instead of closing period(period 13) assetGlobal.setUniversityFiscalPeriodName(""); } } // CSU 6702 END return assetGlobal; } /** * @see org.kuali.kfs.module.cab.document.service.GlLineService#createAssetPaymentDocument(org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntry, java.lang.Integer) */ @Override @NonTransactional public Document createAssetPaymentDocument(GeneralLedgerEntry primaryGlEntry, Integer capitalAssetLineNumber) throws WorkflowException { // Find out the GL Entry // initiate a new document AssetPaymentDocument document = (AssetPaymentDocument) documentService.getNewDocument(DocumentTypeName.ASSET_PAYMENT); document.setCapitalAssetBuilderOriginIndicator(true); //populate the capital asset line distribution amount code to the payment document. CapitalAssetInformation capitalAssetInformation = findCapitalAssetInformation(primaryGlEntry.getDocumentNumber(), capitalAssetLineNumber); if (ObjectUtils.isNotNull(capitalAssetInformation)) { // If this was a shell Capital Asset Information record (for example GL Entries from enterprise feed or Vendor Credit Memo) // setup asset allocation info accordingly so it can be changed on Asset Payment Document if (ObjectUtils.isNull(capitalAssetInformation.getDistributionAmountCode())) { document.setAssetPaymentAllocationTypeCode(KFSConstants.CapitalAssets.DISTRIBUTE_COST_EQUALLY_CODE); document.setAllocationFromFPDocuments(false); } else { document.setAssetPaymentAllocationTypeCode(capitalAssetInformation.getDistributionAmountCode()); document.setAllocationFromFPDocuments(true); } } document.getDocumentHeader().setDocumentDescription(CAB_DESC_PREFIX + primaryGlEntry.getDocumentNumber()); updatePreTagInformation(primaryGlEntry, document, capitalAssetLineNumber); // Asset Payment Detail - sourceAccountingLines on the document.... document.getSourceAccountingLines().addAll(createAssetPaymentDetails(primaryGlEntry, document, 0, capitalAssetLineNumber)); KualiDecimal assetAmount = KualiDecimal.ZERO; List<SourceAccountingLine> sourceAccountingLines = document.getSourceAccountingLines(); for (SourceAccountingLine sourceAccountingLine : sourceAccountingLines) { assetAmount = assetAmount.add(sourceAccountingLine.getAmount()); } List<AssetPaymentAssetDetail> assetPaymentDetails = document.getAssetPaymentAssetDetail(); for (AssetPaymentAssetDetail assetPaymentDetail : assetPaymentDetails) { assetPaymentDetail.setAllocatedAmount(assetAmount); } // Asset payment asset detail // save the document documentService.saveDocument(document); markCapitalAssetProcessed(primaryGlEntry, capitalAssetLineNumber); deactivateGLEntries(primaryGlEntry, document, capitalAssetLineNumber); return document; } /** * Updates pre tag information received from FP document * * @param entry GeneralLedgerEntry * @param document AssetPaymentDocument */ protected void updatePreTagInformation(GeneralLedgerEntry entry, AssetPaymentDocument document, Integer capitalAssetLineNumber) { CapitalAssetInformation capitalAssetInformation = findCapitalAssetInformation(entry.getDocumentNumber(), capitalAssetLineNumber); if (ObjectUtils.isNotNull(capitalAssetInformation)) { //if it is modify asset... if (KFSConstants.CapitalAssets.CAPITAL_ASSET_MODIFY_ACTION_INDICATOR.equals(capitalAssetInformation.getCapitalAssetActionIndicator())) { AssetPaymentAssetDetail assetPaymentAssetDetail = new AssetPaymentAssetDetail(); assetPaymentAssetDetail.setDocumentNumber(document.getDocumentNumber()); // get the allocated amount for the capital asset.... assetPaymentAssetDetail.setCapitalAssetNumber(capitalAssetInformation.getCapitalAssetNumber()); assetPaymentAssetDetail.setAllocatedAmount(KualiDecimal.ZERO); assetPaymentAssetDetail.setAllocatedUserValue(assetPaymentAssetDetail.getAllocatedAmount()); assetPaymentAssetDetail.refreshReferenceObject(CamsPropertyConstants.AssetPaymentAssetDetail.ASSET); Asset asset = assetPaymentAssetDetail.getAsset(); if (ObjectUtils.isNotNull(asset)) { assetPaymentAssetDetail.setPreviousTotalCostAmount(asset.getTotalCostAmount() != null ? asset.getTotalCostAmount() : KualiDecimal.ZERO); document.getAssetPaymentAssetDetail().add(assetPaymentAssetDetail); } } } } /** * Creates asset payment details based on accounting lines distributed * for the given capital asset. * @param entry * @param document * @param seqNo * @param capitalAssetLineNumber * @return List<AssetPaymentDetail> */ protected List<AssetPaymentDetail> createAssetPaymentDetails(GeneralLedgerEntry entry, Document document, int seqNo, Integer capitalAssetLineNumber) { List<AssetPaymentDetail> appliedPayments = new ArrayList<AssetPaymentDetail>(); CapitalAssetInformation capitalAssetInformation = findCapitalAssetInformation(entry.getDocumentNumber(), capitalAssetLineNumber); Collection<GeneralLedgerEntry> documentGlEntries = findAllGeneralLedgerEntry(entry.getDocumentNumber()); if (ObjectUtils.isNotNull(capitalAssetInformation)) { List<CapitalAssetAccountsGroupDetails> groupAccountingLines = capitalAssetInformation.getCapitalAssetAccountsGroupDetails(); Integer paymentSequenceNumber = 1; for (CapitalAssetAccountsGroupDetails accountingLine : groupAccountingLines) { AssetPaymentDetail detail = new AssetPaymentDetail(); // find matching gl entry for asset accounting line for (GeneralLedgerEntry glEntry : documentGlEntries){ if(doesGeneralLedgerEntryMatchAssetAccountingDetails(glEntry, accountingLine)) { entry = glEntry; } } //TODO // sub-object code, as well as sub-account, project code, and org ref id, shall not be populated from GL entry; // instead, they need to be passed from the original FP document for each individual accounting line to be stored in CapitalAssetAccountsGroupDetails, // and copied into each corresponding accounting line in Asset Payment here. detail.setDocumentNumber(document.getDocumentNumber()); detail.setSequenceNumber(paymentSequenceNumber++); detail.setPostingYear(entry.getUniversityFiscalYear()); detail.setPostingPeriodCode(entry.getUniversityFiscalPeriodCode()); detail.setChartOfAccountsCode(accountingLine.getChartOfAccountsCode()); detail.setAccountNumber(replaceFiller(accountingLine.getAccountNumber())); detail.setSubAccountNumber(replaceFiller(accountingLine.getSubAccountNumber())); detail.setFinancialObjectCode(replaceFiller(accountingLine.getFinancialObjectCode())); detail.setFinancialSubObjectCode(replaceFiller(accountingLine.getFinancialSubObjectCode())); detail.setProjectCode(replaceFiller(accountingLine.getProjectCode())); detail.setOrganizationReferenceId(replaceFiller(accountingLine.getOrganizationReferenceId())); //detail.setAmount(KFSConstants.GL_CREDIT_CODE.equals(debitCreditCode) ? accountingLine.getAmount().negated() : accountingLine.getAmount()); detail.setAmount(getAccountingLineAmountForPaymentDetail(entry, accountingLine)); detail.setExpenditureFinancialSystemOriginationCode(replaceFiller(entry.getFinancialSystemOriginationCode())); detail.setExpenditureFinancialDocumentNumber(entry.getDocumentNumber()); detail.setExpenditureFinancialDocumentTypeCode(replaceFiller(entry.getFinancialDocumentTypeCode())); detail.setExpenditureFinancialDocumentPostedDate(entry.getTransactionDate()); detail.setPurchaseOrderNumber(replaceFiller(entry.getReferenceFinancialDocumentNumber())); detail.setTransferPaymentIndicator(false); detail.refreshNonUpdateableReferences(); appliedPayments.add(detail); } } return appliedPayments; } /** * * @param entry GL entry * @param accountingLine accounting line in the capital asset * @return accountingLineAmount */ protected KualiDecimal getAccountingLineAmountForPaymentDetail(GeneralLedgerEntry entry, CapitalAssetAccountsGroupDetails accountingLine) { KualiDecimal accountLineAmount = accountingLine.getAmount(); List<String> expenseObjectTypes = objectTypeService.getExpenseAndTransferObjectTypesForPayments(); List<String> incomeObjectTypes = objectTypeService.getIncomeAndTransferObjectTypesForPayments(); //we are dealing with error correction document so the from amount line should become positive. if (isDocumentAnErrorCorrection(entry)) { if (KFSConstants.SOURCE_ACCT_LINE_TYPE_CODE.equals(accountingLine.getFinancialDocumentLineTypeCode())) { return accountLineAmount.negated(); } return accountLineAmount; } if (expenseObjectTypes.contains(entry.getFinancialObjectTypeCode()) && KFSConstants.SOURCE_ACCT_LINE_TYPE_CODE.equals(accountingLine.getFinancialDocumentLineTypeCode()) && KFSConstants.GL_CREDIT_CODE.equals(entry.getTransactionDebitCreditCode()) && accountLineAmount.compareTo(KualiDecimal.ZERO) > 0) { return accountLineAmount.negated(); } if (incomeObjectTypes.contains(entry.getFinancialObjectTypeCode()) && accountLineAmount.compareTo(KualiDecimal.ZERO) > 0) { return accountLineAmount.negated(); } return accountLineAmount; } /** * determines if the document is an error correction document... * @param entry * @return true if the document is an error correction else false */ protected boolean isDocumentAnErrorCorrection(GeneralLedgerEntry entry) { DocumentHeader docHeader = documentHeaderService.getDocumentHeaderById(entry.getDocumentNumber()); FinancialSystemDocumentHeader fsDocumentHeader = (FinancialSystemDocumentHeader) docHeader; return fsDocumentHeader != null && StringUtils.isNotBlank(fsDocumentHeader.getFinancialDocumentInErrorNumber()); } /** * updates the submit amount by the amount on the accounting line. When submit amount equals * transaction ledger amount, the activity status code is marked as in route status. * @param matchingGLEntry * @param accountLineAmount */ protected void updateTransactionSumbitGlEntryAmount(GeneralLedgerEntry matchingGLEntry, KualiDecimal accountLineAmount) { //update submitted amount on the gl entry and save the results. KualiDecimal submitTotalAmount = KualiDecimal.ZERO; if (ObjectUtils.isNotNull(matchingGLEntry.getTransactionLedgerSubmitAmount())) { submitTotalAmount = matchingGLEntry.getTransactionLedgerSubmitAmount(); } matchingGLEntry.setTransactionLedgerSubmitAmount(submitTotalAmount.add(accountLineAmount.abs())); if (matchingGLEntry.getTransactionLedgerSubmitAmount().equals(matchingGLEntry.getTransactionLedgerEntryAmount())) { matchingGLEntry.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE); } //save the updated gl entry in CAB businessObjectService.save(matchingGLEntry); } /** * NOTE: This method is not used anywhere in project. * Creates asset payment detail based on GL line. to CAB * * @param entry GeneralLedgerEntry * @param document Document * @return AssetPaymentDetail */ protected AssetPaymentDetail createAssetPaymentDetail(GeneralLedgerEntry entry, Document document, int seqNo, Integer capitalAssetLineNumber) { // This is not added to constructor in CAMS to provide module isolation from CAMS AssetPaymentDetail detail = new AssetPaymentDetail(); detail.setDocumentNumber(document.getDocumentNumber()); detail.setSequenceNumber(seqNo); detail.setPostingYear(entry.getUniversityFiscalYear()); detail.setPostingPeriodCode(entry.getUniversityFiscalPeriodCode()); detail.setChartOfAccountsCode(entry.getChartOfAccountsCode()); detail.setAccountNumber(replaceFiller(entry.getAccountNumber())); detail.setSubAccountNumber(replaceFiller(entry.getSubAccountNumber())); detail.setFinancialObjectCode(replaceFiller(entry.getFinancialObjectCode())); detail.setFinancialSubObjectCode(replaceFiller(entry.getFinancialSubObjectCode())); detail.setProjectCode(replaceFiller(entry.getProjectCode())); detail.setOrganizationReferenceId(replaceFiller(entry.getOrganizationReferenceId())); KualiDecimal capitalAssetAmount = getCapitalAssetAmount(entry, capitalAssetLineNumber); detail.setAmount(capitalAssetAmount); detail.setExpenditureFinancialSystemOriginationCode(replaceFiller(entry.getFinancialSystemOriginationCode())); detail.setExpenditureFinancialDocumentNumber(entry.getDocumentNumber()); detail.setExpenditureFinancialDocumentTypeCode(replaceFiller(entry.getFinancialDocumentTypeCode())); detail.setExpenditureFinancialDocumentPostedDate(entry.getTransactionDate()); detail.setPurchaseOrderNumber(replaceFiller(entry.getReferenceFinancialDocumentNumber())); detail.setTransferPaymentIndicator(false); return detail; } /** * retrieves the amount from the capital asset * @param entry * @param capitalAssetLineNumber * @return capital asset amount. */ protected KualiDecimal getCapitalAssetAmount(GeneralLedgerEntry entry, Integer capitalAssetLineNumber) { CapitalAssetInformation capitalAssetInformation = findCapitalAssetInformation(entry.getDocumentNumber(), capitalAssetLineNumber); if (ObjectUtils.isNotNull(capitalAssetInformation)) { return capitalAssetInformation.getCapitalAssetLineAmount(); } return KualiDecimal.ZERO; } /** * If the value contains only the filler characters, then return blank * * @param val Value * @return blank if value if a filler */ protected String replaceFiller(String val) { if (val == null) { return ""; } char[] charArray = val.trim().toCharArray(); for (char c : charArray) { if (c != '-') { return val; } } return ""; } /** * Setup shell Capital Asset Information where it doesn't already exist (for example GL Entries * from enterprise feed or Vendor Credit Memo) * * @param entry */ @Override @Transactional public void setupCapitalAssetInformation(GeneralLedgerEntry entry) { List<CapitalAccountingLines> capitalAccountingLines; int nextCapitalAssetLineNumber = capitalAssetInformationDao.getNextCapitalAssetLineNumber(entry.getDocumentNumber()); capitalAccountingLines = new ArrayList<CapitalAccountingLines>(); createCapitalAccountingLine(capitalAccountingLines, entry, null); createNewCapitalAsset(capitalAccountingLines,entry.getDocumentNumber(),null,nextCapitalAssetLineNumber); } protected List<CapitalAccountingLines> createCapitalAccountingLine(List<CapitalAccountingLines> capitalAccountingLines, GeneralLedgerEntry entry, String distributionAmountCode) { Integer sequenceNumber = capitalAccountingLines.size() + 1; //capital object code so we need to build the capital accounting line... CapitalAccountingLines cal = addCapitalAccountingLine(capitalAccountingLines, entry); cal.setDistributionAmountCode(distributionAmountCode); capitalAccountingLines.add(cal); return capitalAccountingLines; } /** * convenience method to add a new capital accounting line to the collection of capital * accounting lines. * * @param capitalAccountingLines * @param entry * @return */ protected CapitalAccountingLines addCapitalAccountingLine(List<CapitalAccountingLines> capitalAccountingLines, GeneralLedgerEntry entry) { CapitalAccountingLines cal = new CapitalAccountingLines(); String capitalAssetLineType = KFSConstants.GL_CREDIT_CODE.equals(entry.getTransactionDebitCreditCode()) ? KFSConstants.SOURCE : KFSConstants.TARGET; cal.setLineType(capitalAssetLineType); cal.setSequenceNumber(entry.getTransactionLedgerEntrySequenceNumber()); cal.setChartOfAccountsCode(entry.getChartOfAccountsCode()); cal.setAccountNumber(entry.getAccountNumber()); cal.setSubAccountNumber(entry.getSubAccountNumber()); cal.setFinancialObjectCode(entry.getFinancialObjectCode()); cal.setFinancialSubObjectCode(entry.getFinancialSubObjectCode()); cal.setProjectCode(entry.getProjectCode()); cal.setOrganizationReferenceId(entry.getOrganizationReferenceId()); cal.setFinancialDocumentLineDescription(entry.getTransactionLedgerEntryDescription()); cal.setAmount(entry.getAmount()); cal.setAccountLinePercent(null); cal.setSelectLine(false); return cal; } /** * helper method to add accounting details for this new capital asset record * * @param capitalAccountingLines * @param currentCapitalAssetInformation * @param documentNumber * @param actionType * @param nextCapitalAssetLineNumnber */ protected void createNewCapitalAsset(List<CapitalAccountingLines> capitalAccountingLines, String documentNumber, String actionType, Integer nextCapitalAssetLineNumber) { CapitalAssetInformation capitalAsset = new CapitalAssetInformation(); capitalAsset.setCapitalAssetLineAmount(KualiDecimal.ZERO); capitalAsset.setDocumentNumber(documentNumber); capitalAsset.setCapitalAssetLineNumber(nextCapitalAssetLineNumber); capitalAsset.setCapitalAssetActionIndicator(actionType); capitalAsset.setCapitalAssetProcessedIndicator(false); KualiDecimal capitalAssetLineAmount = KualiDecimal.ZERO; //now setup the account line information associated with this capital asset for (CapitalAccountingLines capitalAccountingLine : capitalAccountingLines) { capitalAsset.setDistributionAmountCode(capitalAccountingLine.getDistributionAmountCode()); createCapitalAssetAccountingLinesDetails(capitalAccountingLine, capitalAsset); capitalAssetLineAmount = capitalAssetLineAmount.add(capitalAccountingLine.getAmount()); } capitalAsset.setCapitalAssetLineAmount(capitalAssetLineAmount); businessObjectService.save(capitalAsset); } /** * * * @param capitalAccountingLine * @param capitalAsset */ protected void createCapitalAssetAccountingLinesDetails(CapitalAccountingLines capitalAccountingLine, CapitalAssetInformation capitalAsset) { //now setup the account line information associated with this capital asset CapitalAssetAccountsGroupDetails capitalAssetAccountLine = new CapitalAssetAccountsGroupDetails(); capitalAssetAccountLine.setDocumentNumber(capitalAsset.getDocumentNumber()); capitalAssetAccountLine.setChartOfAccountsCode(capitalAccountingLine.getChartOfAccountsCode()); capitalAssetAccountLine.setAccountNumber(capitalAccountingLine.getAccountNumber()); capitalAssetAccountLine.setSubAccountNumber(capitalAccountingLine.getSubAccountNumber()); capitalAssetAccountLine.setFinancialDocumentLineTypeCode(KFSConstants.SOURCE.equals(capitalAccountingLine.getLineType()) ? KFSConstants.SOURCE_ACCT_LINE_TYPE_CODE : KFSConstants.TARGET_ACCT_LINE_TYPE_CODE); capitalAssetAccountLine.setCapitalAssetAccountLineNumber(getNextAccountingLineNumber(capitalAccountingLine, capitalAsset)); capitalAssetAccountLine.setCapitalAssetLineNumber(capitalAsset.getCapitalAssetLineNumber()); capitalAssetAccountLine.setFinancialObjectCode(capitalAccountingLine.getFinancialObjectCode()); capitalAssetAccountLine.setFinancialSubObjectCode(capitalAccountingLine.getFinancialSubObjectCode()); capitalAssetAccountLine.setProjectCode(capitalAccountingLine.getProjectCode()); capitalAssetAccountLine.setOrganizationReferenceId(capitalAccountingLine.getOrganizationReferenceId()); capitalAssetAccountLine.setSequenceNumber(capitalAccountingLine.getSequenceNumber()); capitalAssetAccountLine.setAmount(capitalAccountingLine.getAmount()); capitalAsset.getCapitalAssetAccountsGroupDetails().add(capitalAssetAccountLine); } /** * calculates the next accounting line number for accounts details for each capital asset. * Goes through the current records and gets the last accounting line number. * * @param capitalAsset * @return nextAccountingLineNumber */ protected Integer getNextAccountingLineNumber(CapitalAccountingLines capitalAccountingLine, CapitalAssetInformation capitalAsset) { Integer nextAccountingLineNumber = 0; List<CapitalAssetAccountsGroupDetails> capitalAssetAccountLines = capitalAsset.getCapitalAssetAccountsGroupDetails(); for (CapitalAssetAccountsGroupDetails capitalAssetAccountLine : capitalAssetAccountLines) { nextAccountingLineNumber = capitalAssetAccountLine.getCapitalAssetAccountLineNumber(); } return ++nextAccountingLineNumber; } @NonTransactional public void setBusinessObjectService(BusinessObjectService businessObjectService) { this.businessObjectService = businessObjectService; } @NonTransactional public void setAssetGlobalService(AssetGlobalService assetGlobalService) { this.assetGlobalService = assetGlobalService; } @NonTransactional public void setObjectTypeService(ObjectTypeService objectTypeService) { this.objectTypeService = objectTypeService; } @NonTransactional public void setDocumentService(DocumentService documentService) { this.documentService = documentService; } @NonTransactional public void setParameterService(ParameterService parameterService) { this.parameterService = parameterService; } @NonTransactional public void setParameterEvaluatorService(ParameterEvaluatorService parameterEvaluatorService) { this.parameterEvaluatorService = parameterEvaluatorService; } @NonTransactional public void setDocumentHeaderService(DocumentHeaderService documentHeaderService) { this.documentHeaderService = documentHeaderService; } @NonTransactional public void setCapitalAssetInformationDao(CapitalAssetInformationDao dao) { this.capitalAssetInformationDao = dao; } }