/*
* 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.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.integration.purap.CapitalAssetLocation;
import org.kuali.kfs.integration.purap.ItemCapitalAsset;
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.Pretag;
import org.kuali.kfs.module.cab.businessobject.PretagDetail;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableItemAsset;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableLineAssetAccount;
import org.kuali.kfs.module.cab.document.service.PurApInfoService;
import org.kuali.kfs.module.cab.document.service.PurApLineDocumentService;
import org.kuali.kfs.module.cab.document.service.PurApLineService;
import org.kuali.kfs.module.cab.document.web.PurApLineSession;
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.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.AssetType;
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.document.service.AssetService;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.businessobject.PurchaseOrderCapitalAssetSystem;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.sys.businessobject.Building;
import org.kuali.kfs.sys.businessobject.Room;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.validation.event.DocumentSystemSaveEvent;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kns.document.MaintenanceDocument;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.location.api.campus.Campus;
import org.kuali.rice.location.api.campus.CampusService;
/**
* This class provides default implementations of {@link PurApLineService}
*/
public class PurApLineDocumentServiceImpl implements PurApLineDocumentService {
private static final Logger LOG = Logger.getLogger(PurApLineDocumentServiceImpl.class);
private BusinessObjectService businessObjectService;
private DocumentService documentService;
private PurApLineService purApLineService;
private PurApInfoService purApInfoService;
private AssetGlobalService assetGlobalService;
public static final String DOCUMENT_DESC_PREFIX = "CAB created for ";
/**
* @see org.kuali.kfs.module.cab.document.service.PurApLineDocumentService#processApplyPayment(PurchasingAccountsPayableItemAsset,
* List, PurApLineSession, Integer)
*/
@Override
public String processApplyPayment(PurchasingAccountsPayableItemAsset selectedItem, List<PurchasingAccountsPayableDocument> purApDocs, PurApLineSession purApLineSession, Integer requisitionIdentifer) throws WorkflowException {
AssetPaymentDocument newDocument = (AssetPaymentDocument) documentService.getNewDocument(AssetPaymentDocument.class);
if (ObjectUtils.isNotNull(selectedItem) && ObjectUtils.isNotNull(selectedItem.getPurchasingAccountsPayableDocument())) {
newDocument.getDocumentHeader().setDocumentDescription(DOCUMENT_DESC_PREFIX + selectedItem.getPurchasingAccountsPayableDocument().getDocumentTypeCode() + " " + selectedItem.getDocumentNumber());
}
// set assetPaymentDetail list
createAssetPaymentDetails(newDocument.getSourceAccountingLines(), selectedItem, newDocument.getDocumentNumber(), requisitionIdentifer);
// If PurAp user entered capitalAssetNumbers, include them in the Asset Payment Document.
if (selectedItem.getPurApItemAssets() != null && !selectedItem.getPurApItemAssets().isEmpty()) {
createAssetPaymentAssetDetails(newDocument.getAssetPaymentAssetDetail(), selectedItem, newDocument.getDocumentNumber());
}
// set origin code in the Asset Payment Document
newDocument.setCapitalAssetBuilderOriginIndicator(true);
documentService.saveDocument(newDocument);
postProcessCreatingDocument(selectedItem, purApDocs, purApLineSession, newDocument.getDocumentNumber());
return newDocument.getDocumentNumber();
}
/**
* Create AssetPaymentAssetDetail List for assetPaymentDocument.
*
* @param assetPaymentAssetDetails
* @param selectedItem
* @param documentNumber
*/
protected void createAssetPaymentAssetDetails(List assetPaymentAssetDetails, PurchasingAccountsPayableItemAsset selectedItem, String documentNumber) {
for (ItemCapitalAsset capitalAssetNumber : selectedItem.getPurApItemAssets()) {
// check if capitalAssetNumber is a valid value or not.
if (isAssetNumberValid(capitalAssetNumber.getCapitalAssetNumber())) {
AssetPaymentAssetDetail assetDetail = new AssetPaymentAssetDetail();
assetDetail.setDocumentNumber(documentNumber);
assetDetail.setCapitalAssetNumber(capitalAssetNumber.getCapitalAssetNumber());
assetDetail.refreshReferenceObject(CamsPropertyConstants.AssetPaymentAssetDetail.ASSET);
AssetService assetService = SpringContext.getBean(AssetService.class);
Asset candidateAsset = assetDetail.getAsset();
// asset must be an active & not retired. Duplication check is done during feeding asset numbers from PurAp.
if (ObjectUtils.isNotNull(candidateAsset) && assetService.isCapitalAsset(candidateAsset) && !assetService.isAssetRetired(candidateAsset)) {
assetDetail.setPreviousTotalCostAmount(assetDetail.getAsset().getTotalCostAmount());
assetPaymentAssetDetails.add(assetDetail);
}
}
}
}
/**
* Check the asset table if given capitalAssetNumber is valid or not.
*
* @param capitalAssetNumber
* @return
*/
protected boolean isAssetNumberValid(Long capitalAssetNumber) {
Map pKeys = new HashMap<String, Object>();
pKeys.put(CamsPropertyConstants.Asset.CAPITAL_ASSET_NUMBER, capitalAssetNumber);
Asset asset = businessObjectService.findByPrimaryKey(Asset.class, pKeys);
return ObjectUtils.isNotNull(asset);
}
/**
* @see org.kuali.kfs.module.cab.document.service.PurApLineService#processCreateAsset(org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableItemAsset,
* org.kuali.kfs.module.cab.document.web.struts.PurApLineForm)
*/
@Override
public String processCreateAsset(PurchasingAccountsPayableItemAsset selectedItem, List<PurchasingAccountsPayableDocument> purApDocs, PurApLineSession purApLineSession, Integer requisitionIdentifier) throws WorkflowException {
// Create new CAMS asset global document
MaintenanceDocument newDocument = (MaintenanceDocument) documentService.getNewDocument(CamsConstants.DocumentTypeName.ASSET_ADD_GLOBAL);
newDocument.getNewMaintainableObject().setMaintenanceAction(KRADConstants.MAINTENANCE_NEW_ACTION);
if (ObjectUtils.isNotNull(selectedItem) && ObjectUtils.isNotNull(selectedItem.getPurchasingAccountsPayableDocument())) {
newDocument.getDocumentHeader().setDocumentDescription(DOCUMENT_DESC_PREFIX + selectedItem.getPurchasingAccountsPayableDocument().getDocumentTypeCode() + " " + selectedItem.getDocumentNumber());
}
// populate pre-tagging entry
Integer poId = selectedItem.getPurchasingAccountsPayableDocument().getPurchaseOrderIdentifier();
Pretag preTag = purApLineService.getPreTagLineItem(poId, selectedItem.getItemLineNumber());
// create asset global BO instance
AssetGlobal assetGlobal = createAssetGlobal(selectedItem, newDocument.getDocumentNumber(), preTag, requisitionIdentifier);
// save asset global BO to the document
newDocument.getNewMaintainableObject().setBusinessObject(assetGlobal);
// save without doing validation - this is just to get us to the asset global maint. screen, the validation will be applied there
documentService.saveDocument(newDocument, DocumentSystemSaveEvent.class);
postProcessCreatingDocument(selectedItem, purApDocs, purApLineSession, newDocument.getDocumentNumber());
// Save for in-active pre-tag detail if it got feed into CAMS
if (isItemPretagged(preTag)) {
businessObjectService.save(preTag);
}
return newDocument.getDocumentNumber();
}
/**
* Process item line, cab document after creating CAMs document.
*
* @param selectedItem
* @param purApForm
* @param purApLineSession
* @param documentNumber
*/
protected void postProcessCreatingDocument(PurchasingAccountsPayableItemAsset selectedItem, List<PurchasingAccountsPayableDocument> purApDocs, PurApLineSession purApLineSession, String documentNumber) {
// save CAMS document number in CAB
selectedItem.setCapitalAssetManagementDocumentNumber(documentNumber);
// in-activate item, item account and glEntry(conditionally)
inActivateItem(selectedItem);
// update submit amount in the associated general ledger entries.
updateGlEntrySubmitAmount(selectedItem, purApLineSession.getGlEntryUpdateList());
// in-activate document if all the associated items are inactive.
if (ObjectUtils.isNotNull(selectedItem.getPurchasingAccountsPayableDocument())) {
// update document status code as 'Enroute' when all its items are in CAMs. Link reference from item to document should
// be set in PurApLineAction.getSelectedLineItem().
conditionalyUpdateDocumentStatusAsEnroute(selectedItem.getPurchasingAccountsPayableDocument());
}
// persistent to the table
purApLineService.processSaveBusinessObjects(purApDocs, purApLineSession);
// In-activate general ledger afterwards because we don't maintain the non-persistent relationship from GL to account, so
// account need to persistent changes first.
List<GeneralLedgerEntry> glEntryUpdatesList = getGlEntryInActivedList(selectedItem);
if (glEntryUpdatesList != null && !glEntryUpdatesList.isEmpty()) {
businessObjectService.save(glEntryUpdatesList);
}
}
/**
* set doc status as enroute when all its items are in CAMs
*
* @param selectedDoc
*/
protected void conditionalyUpdateDocumentStatusAsEnroute(PurchasingAccountsPayableDocument selectedDoc) {
for (PurchasingAccountsPayableItemAsset item : selectedDoc.getPurchasingAccountsPayableItemAssets()) {
if (item.isActive()) {
return;
}
}
selectedDoc.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
}
/**
* Update transactionLedgerSubmitAmount in the associated generalLedgerEntry for each item account.
*
* @param selectedItem
*/
protected void updateGlEntrySubmitAmount(PurchasingAccountsPayableItemAsset selectedItem, List glEntryList) {
GeneralLedgerEntry glEntry = null;
for (PurchasingAccountsPayableLineAssetAccount account : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
glEntry = account.getGeneralLedgerEntry();
if (ObjectUtils.isNotNull(glEntry)) {
// Add account amount to GL entry submit amount.
if (glEntry.getTransactionLedgerSubmitAmount() != null) {
glEntry.setTransactionLedgerSubmitAmount(glEntry.getTransactionLedgerSubmitAmount().add(account.getItemAccountTotalAmount()));
}
else {
glEntry.setTransactionLedgerSubmitAmount(new KualiDecimal(account.getItemAccountTotalAmount().toString()));
}
}
// add to the session for persistence
glEntryList.add(glEntry);
}
}
/**
* Build asset details/shared details/unique details lists for new asset global document
*
* @param selectedItem
* @param newDocument
* @param assetGlobal
*/
protected void setAssetGlobalDetails(PurchasingAccountsPayableItemAsset selectedItem, AssetGlobal assetGlobal, Pretag preTag, PurchaseOrderCapitalAssetSystem capitalAssetSystem) {
// build assetGlobalDetail list( will be used for creating unique details list at the same time)
List<AssetGlobalDetail> assetDetailsList = assetGlobal.getAssetGlobalDetails();
// shared location details list
List<AssetGlobalDetail> sharedDetails = assetGlobal.getAssetSharedDetails();
for (int i = 0; i < selectedItem.getAccountsPayableItemQuantity().intValue(); i++) {
AssetGlobalDetail assetGlobalDetail = new AssetGlobalDetail();
assetGlobalDetail.setDocumentNumber(assetGlobal.getDocumentNumber());
assetGlobalDetail.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue());
assetDetailsList.add(assetGlobalDetail);
// build assetSharedDetails and assetGlobalUniqueDetails list. There two lists will be used to rebuild
// assetGlobalDetails list when AssetGlobalMaintainableImpl.prepareForSave() is called during save the document.
AssetGlobalDetail sharedDetail = new AssetGlobalDetail();
// added as unique detail
sharedDetail.getAssetGlobalUniqueDetails().add(assetGlobalDetail);
sharedDetails.add(sharedDetail);
}
// feeding data from pre-tag details into shared location details list and unique detail list
if (isItemPretagged(preTag)) {
setAssetDetailFromPreTag(preTag, sharedDetails, assetDetailsList);
}
// feeding location data from PurAp into assetGlobalDetail List.
if (!isItemFullyPretagged(preTag, assetGlobal) && ObjectUtils.isNotNull(capitalAssetSystem)) {
setAssetGlobalDetailFromPurAp(capitalAssetSystem, sharedDetails);
}
}
/**
* Check if the all new assets get pre-tagged by pre-tagging.
*
* @param preTag
* @param assetGlobal
* @return
*/
protected boolean isItemFullyPretagged(Pretag preTag, AssetGlobal assetGlobal) {
if (isItemPretagged(preTag)) {
List<PretagDetail> pretagDetails = preTag.getPretagDetails();
int pretagSize = 0;
for (PretagDetail pretagDetail : pretagDetails) {
if (pretagDetail.isActive()) {
pretagSize++;
}
}
return pretagSize >= assetGlobal.getAssetSharedDetails().size();
}
return false;
}
/**
* Set asset global detail location information from PurAp input. In this method, no grouping for shared location because
* AssetGlobalMaintainableImpl.processAfterRetrieve() will group the shared location anyway...
*
* @param capitalAssetSystem
* @param assetDetailsList
*/
protected void setAssetGlobalDetailFromPurAp(PurchaseOrderCapitalAssetSystem capitalAssetSystem, List<AssetGlobalDetail> assetSharedDetail) {
List<CapitalAssetLocation> capitalAssetLocations = capitalAssetSystem.getCapitalAssetLocations();
if (ObjectUtils.isNotNull(capitalAssetLocations) && !capitalAssetLocations.isEmpty()) {
Iterator<CapitalAssetLocation> locationIterator = capitalAssetLocations.iterator();
int locationQuantity = 0;
CapitalAssetLocation assetLocation = null;
for (AssetGlobalDetail assetDetail : assetSharedDetail) {
// if it's already pre-tagged, skip it.
if (StringUtils.isNotEmpty(assetDetail.getCampusCode())) {
continue;
}
// Each line item can have multiple locations and each location can have a quantity value with it.
if (locationQuantity <= 0 && locationIterator.hasNext()) {
// when we consume the current location quantity, we need to move to the next PurAp location.
assetLocation = locationIterator.next();
// initialize location quantity by PurAp setting
if (assetLocation.getItemQuantity() != null) {
locationQuantity = assetLocation.getItemQuantity().intValue();
}
else {
// if Purap not set item quantity, we set it to 1.
locationQuantity = 1;
}
}
else if (locationQuantity <= 0 && !locationIterator.hasNext()) {
// Consume the current location quantity and no more PurAp locations can be used, stop here.
break;
}
// set PurAp asset location into asset global document
setNewAssetByPurApLocation(assetLocation, assetDetail);
locationQuantity--;
}
}
}
/**
* Set asset global detail by PurAp asset location.
*
* @param assetLocation
* @param assetDetail
*/
protected void setNewAssetByPurApLocation(CapitalAssetLocation assetLocation, AssetGlobalDetail assetDetail) {
String campusCode = assetLocation.getCampusCode();
// Set campus code only when it is a valid value. Otherwise, when save document, invalid data will violate data integrity
// and block save.
if (!StringUtils.isBlank(campusCode) && checkCampusCodeValid(campusCode)) {
assetDetail.setCampusCode(campusCode);
// for on-campus
if (!assetLocation.isOffCampusIndicator()) {
String buildingCode = assetLocation.getBuildingCode();
// Set building code only when it is a valid value. Otherwise, when save document, invalid data will violate data
// integrity and block save.
if (!StringUtils.isBlank(buildingCode) && checkBuildingCodeValid(campusCode, buildingCode)) {
assetDetail.setBuildingCode(buildingCode);
String buildingRoomNumber = assetLocation.getBuildingRoomNumber();
// Set building room number only when it is a valid value. Otherwise, when save document, invalid data will
// violate data integrity and block save.
if (!StringUtils.isBlank(buildingRoomNumber) && checkBuildingRoomNumberValid(campusCode, buildingCode, buildingRoomNumber)) {
assetDetail.setBuildingRoomNumber(buildingRoomNumber);
}
}
}
else {
// off-campus
assetDetail.setOffCampusCityName(assetLocation.getCapitalAssetCityName());
assetDetail.setOffCampusAddress(assetLocation.getCapitalAssetLine1Address());
assetDetail.setOffCampusCountryCode(assetLocation.getCapitalAssetCountryCode());
assetDetail.setOffCampusStateCode(assetLocation.getCapitalAssetStateCode());
assetDetail.setOffCampusZipCode(assetLocation.getCapitalAssetPostalCode());
}
}
}
/**
* Check the given buildingCode and campusCode valid.
*
* @param campusCode
* @param buildingCode
* @param buildingRoomNumber
* @return
*/
protected boolean checkBuildingRoomNumberValid(String campusCode, String buildingCode, String buildingRoomNumber) {
Map<String, Object> pKeys = new HashMap<String, Object>();
pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAMPUS_CODE, campusCode);
pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.BUILDING_CODE, buildingCode);
pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.BUILDING_ROOM_NUMBER, buildingRoomNumber);
Room room = this.getBusinessObjectService().findByPrimaryKey(Room.class, pKeys);
return ObjectUtils.isNotNull(room) && room.isActive();
}
/**
* Check the given buildingCode and campusCode valid.
*
* @param buildingCode
* @return
*/
protected boolean checkBuildingCodeValid(String campusCode, String buildingCode) {
Map<String, Object> pKeys = new HashMap<String, Object>();
pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAMPUS_CODE, campusCode);
pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.BUILDING_CODE, buildingCode);
Building building = this.getBusinessObjectService().findByPrimaryKey(Building.class, pKeys);
return ObjectUtils.isNotNull(building) && building.isActive();
}
/**
* check the given campus code existing and active status.
*
* @param campusCode
* @return
*/
protected boolean checkCampusCodeValid(String campusCode) {
Map<String, Object> criteria = new HashMap<String, Object>();
criteria.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAMPUS_CODE, campusCode);
Campus campus = SpringContext.getBean(CampusService.class).getCampus(campusCode/*RICE_20_REFACTORME criteria */);
return ObjectUtils.isNotNull(campus) && campus.isActive();
}
/**
* Feeding data into assetGlobalDetail list from preTagDetail
*
* @param preTag
* @param assetDetailsList
*/
protected void setAssetDetailFromPreTag(Pretag preTag, List<AssetGlobalDetail> assetSharedDetails, List<AssetGlobalDetail> assetUniqueDetails) {
Iterator<AssetGlobalDetail> sharedDetailsIterator = assetSharedDetails.iterator();
Iterator<AssetGlobalDetail> uniqueDetailsIterator = assetUniqueDetails.iterator();
for (PretagDetail preTagDetail : preTag.getPretagDetails()) {
if (!preTagDetail.isActive()) {
continue;
}
if (sharedDetailsIterator.hasNext()) {
// set shared location details
AssetGlobalDetail sharedDetail = sharedDetailsIterator.next();
sharedDetail.setBuildingCode(preTagDetail.getBuildingCode());
sharedDetail.setBuildingRoomNumber(preTagDetail.getBuildingRoomNumber());
sharedDetail.setBuildingSubRoomNumber(preTagDetail.getBuildingSubRoomNumber());
sharedDetail.setCampusCode(preTagDetail.getCampusCode());
// In-activate pre-tagging detail, and will be persistent to the DB.
preTagDetail.setActive(false);
}
if (uniqueDetailsIterator.hasNext()) {
// set asset unique detail
AssetGlobalDetail uniqueDetail = uniqueDetailsIterator.next();
uniqueDetail.setGovernmentTagNumber(preTagDetail.getGovernmentTagNumber());
uniqueDetail.setNationalStockNumber(preTagDetail.getNationalStockNumber());
uniqueDetail.setCampusTagNumber(preTagDetail.getCampusTagNumber());
uniqueDetail.setOrganizationInventoryName(preTag.getOrganizationInventoryName());
uniqueDetail.setSerialNumber(preTagDetail.getSerialNumber());
uniqueDetail.setRepresentativeUniversalIdentifier(preTag.getRepresentativeUniversalIdentifier());
// In-activate pre-tagging detail and will be persistent to the DB.
preTagDetail.setActive(false);
}
}
// In-activate preTag if possible.
inActivatePreTag(preTag);
}
/**
* In-activate preTag if all its preTagDetail entry are inactive.
*
* @param preTag
*/
protected void inActivatePreTag(Pretag preTag) {
// get the number of inactive pre-tag detail.
int inActiveCounter = 0;
for (PretagDetail preTagDetail : preTag.getPretagDetails()) {
if (!preTagDetail.isActive()) {
inActiveCounter++;
}
}
// if the number of inactive preTagDetail is equal or greater than (when quantityInvoiced is decimal) quantityInvoiced,
// in-activate the preTag active field.
if (preTag.getQuantityInvoiced().isLessEqual(new KualiDecimal(inActiveCounter))) {
preTag.setActive(false);
}
}
/**
* Build asset payment details list for new asset global document.
*
* @param selectedItem
* @param assetGlobal
* @param requisitionIdentifier
*/
protected void createAssetPaymentDetails(List<AssetPaymentDetail> assetPaymentList, PurchasingAccountsPayableItemAsset selectedItem, String documentNumber, Integer requisitionIdentifier) {
int seq = 1;
for (PurchasingAccountsPayableLineAssetAccount account : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
GeneralLedgerEntry glEntry = account.getGeneralLedgerEntry();
if (ObjectUtils.isNotNull(glEntry)) {
AssetPaymentDetail assetPaymentDetail = new AssetPaymentDetail();
// initialize payment detail fields
assetPaymentDetail.setDocumentNumber(documentNumber);
assetPaymentDetail.setSequenceNumber(Integer.valueOf(seq++));
assetPaymentDetail.setChartOfAccountsCode(glEntry.getChartOfAccountsCode());
assetPaymentDetail.setAccountNumber(replaceFiller(glEntry.getAccountNumber()));
assetPaymentDetail.setSubAccountNumber(replaceFiller(glEntry.getSubAccountNumber()));
assetPaymentDetail.setFinancialObjectCode(replaceFiller(glEntry.getFinancialObjectCode()));
assetPaymentDetail.setFinancialSubObjectCode(replaceFiller(glEntry.getFinancialSubObjectCode()));
assetPaymentDetail.setProjectCode(replaceFiller(glEntry.getProjectCode()));
assetPaymentDetail.setOrganizationReferenceId(glEntry.getOrganizationReferenceId());
assetPaymentDetail.setPostingYear(glEntry.getUniversityFiscalYear());
assetPaymentDetail.setPostingPeriodCode(glEntry.getUniversityFiscalPeriodCode());
assetPaymentDetail.setExpenditureFinancialSystemOriginationCode(replaceFiller(glEntry.getFinancialSystemOriginationCode()));
assetPaymentDetail.setExpenditureFinancialDocumentNumber(glEntry.getDocumentNumber());
assetPaymentDetail.setExpenditureFinancialDocumentTypeCode(replaceFiller(glEntry.getFinancialDocumentTypeCode()));
assetPaymentDetail.setExpenditureFinancialDocumentPostedDate(glEntry.getTransactionDate());
assetPaymentDetail.setAmount(account.getItemAccountTotalAmount());
assetPaymentDetail.setPurchaseOrderNumber(replaceFiller(glEntry.getReferenceFinancialDocumentNumber()));
assetPaymentDetail.setRequisitionNumber(requisitionIdentifier.toString());
assetPaymentDetail.setTransferPaymentIndicator(false);
// add to payment list
assetPaymentList.add(assetPaymentDetail);
}
}
}
/**
* In-activate item, item Account and generalLedgerEntry active indicator.
*
* @param selectedItem
* @param glEntryList
*/
protected void inActivateItem(PurchasingAccountsPayableItemAsset selectedItem) {
// in-active each account.
for (PurchasingAccountsPayableLineAssetAccount selectedAccount : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
selectedAccount.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
}
// in-activate selected Item
selectedItem.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
}
/**
* Update GL Entry status as "enroute" if all its amount are consumed by submit CAMs document.Return the general ledger entry
* changes as a list.
*
* @param glEntryList
* @param selectedAccount
* @param glEntry
*/
protected List<GeneralLedgerEntry> getGlEntryInActivedList(PurchasingAccountsPayableItemAsset selectedItem) {
List<GeneralLedgerEntry> glEntryUpdateList = new ArrayList<GeneralLedgerEntry>();
for (PurchasingAccountsPayableLineAssetAccount selectedAccount : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
GeneralLedgerEntry glEntry = selectedAccount.getGeneralLedgerEntry();
KualiDecimal relatedAccountAmount = KualiDecimal.ZERO;
// get persistent account list which should be save before hand
glEntry.refreshReferenceObject(CabPropertyConstants.GeneralLedgerEntry.PURAP_LINE_ASSET_ACCOUNTS);
// check if all accounts are inactive status
for (PurchasingAccountsPayableLineAssetAccount account : glEntry.getPurApLineAssetAccounts()) {
if (!account.isActive()) {
relatedAccountAmount = relatedAccountAmount.add(account.getItemAccountTotalAmount());
}
}
// if one account shows active, won't in-activate this general ledger entry.
if (relatedAccountAmount.compareTo(glEntry.getAmount()) == 0) {
glEntry.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
glEntryUpdateList.add(glEntry);
}
}
return glEntryUpdateList;
}
protected String replaceFiller(String val) {
return val == null ? "" : val.trim().replaceAll("-", "");
}
/**
* Create AssetGlobal BO and feed data from pre-asset tagging table.
*
* @param selectedItem
* @param newDocument
* @param preTag
* @return
*/
protected AssetGlobal createAssetGlobal(PurchasingAccountsPayableItemAsset selectedItem, String documentNumber, Pretag preTag, Integer requisitionIdentifier) {
// instantiate AssetGlobal BO
AssetGlobal assetGlobal = new AssetGlobal();
assetGlobal.setDocumentNumber(documentNumber);
assetGlobal.setCapitalAssetDescription(selectedItem.getAccountsPayableLineItemDescription());
assetGlobal.setConditionCode(CamsConstants.Asset.CONDITION_CODE_E);
assetGlobal.setAcquisitionTypeCode(getAssetGlobalService().getNewAcquisitionTypeCode());
assetGlobal.setInventoryStatusCode(CamsConstants.InventoryStatusCode.CAPITAL_ASSET_ACTIVE_IDENTIFIABLE);
// set vendor name from Purchase Order Document
PurchaseOrderDocument purApdocument = this.getPurApInfoService().getCurrentDocumentForPurchaseOrderIdentifier(selectedItem.getPurchasingAccountsPayableDocument().getPurchaseOrderIdentifier());
if (purApdocument != null) {
assetGlobal.setVendorName(purApdocument.getVendorName());
}
// set origin code in the Asset Payment Document
assetGlobal.setCapitalAssetBuilderOriginIndicator(true);
PurchaseOrderCapitalAssetSystem capitalAssetSystem = null;
// check and set if purAp has new asset information
if (selectedItem.getCapitalAssetSystemIdentifier() != null) {
capitalAssetSystem = findCapitalAssetSystem(selectedItem.getCapitalAssetSystemIdentifier());
if (ObjectUtils.isNotNull(capitalAssetSystem)) {
setAssetGlobalFromPurAp(assetGlobal, capitalAssetSystem);
}
}
// feeding data from pre-asset tagging table into assetGlboal. Here, if there are data overlap in pretag and PurAp, we
// respect data in Pretagging.
if (isItemPretagged(preTag)) {
setAssetGlobalFromPreTag(preTag, assetGlobal);
}
// set asset global detail list
setAssetGlobalDetails(selectedItem, assetGlobal, preTag, capitalAssetSystem);
// Set Organization Inventory Name for each asset unique detail from PO
setOrgInventoryNameForAssetDetail(assetGlobal.getAssetGlobalDetails(), purApdocument);
// build payments list for asset global
createAssetPaymentDetails(assetGlobal.getAssetPaymentDetails(), selectedItem, documentNumber, requisitionIdentifier);
// set total cost
setAssetGlobalTotalCost(assetGlobal);
// Set Asset Global organization owner account, which is the account that contributed the most dollars.
setAssetGlobalOrgOwnerAccount(assetGlobal);
return assetGlobal;
}
/**
* Set organization inventory name for each asset detail by PO Contact name or if empty, by Requestor Name.
*
* @param assetGlobalDetails
* @param purApdocument
*/
protected void setOrgInventoryNameForAssetDetail(List<AssetGlobalDetail> assetGlobalDetails, PurchaseOrderDocument purApdocument) {
if (ObjectUtils.isNotNull(purApdocument)) {
String orgInventoryName = purApdocument.getInstitutionContactName();
if (StringUtils.isBlank(orgInventoryName)) {
orgInventoryName = purApdocument.getRequestorPersonName();
}
for (AssetGlobalDetail assetGlobalDetail : assetGlobalDetails) {
assetGlobalDetail.setOrganizationInventoryName(orgInventoryName);
}
}
}
/**
* check if item is pre-tagged already.
*
* @param preTag
* @return
*/
protected boolean isItemPretagged(Pretag preTag) {
return ObjectUtils.isNotNull(preTag) && preTag.isActive() && ObjectUtils.isNotNull(preTag.getPretagDetails()) && !preTag.getPretagDetails().isEmpty();
}
/**
* Set asset information from PurAp PurchaseOrderCapitalAssetSystem.
*
* @param assetGlobal
* @param capitalAssetSystem
*/
protected void setAssetGlobalFromPurAp(AssetGlobal assetGlobal, PurchaseOrderCapitalAssetSystem capitalAssetSystem) {
assetGlobal.setManufacturerName(capitalAssetSystem.getCapitalAssetManufacturerName());
assetGlobal.setManufacturerModelNumber(capitalAssetSystem.getCapitalAssetModelDescription());
String capitalAssetTypeCode = capitalAssetSystem.getCapitalAssetTypeCode();
if (!StringUtils.isBlank(capitalAssetTypeCode) && checkCapitalAssetTypeCodeExist(capitalAssetTypeCode)) {
assetGlobal.setCapitalAssetTypeCode(capitalAssetSystem.getCapitalAssetTypeCode());
}
}
/**
* check the given capital asset type code exists in CAM
*
* @param capitalAssetTypeCode
* @return
*/
protected boolean checkCapitalAssetTypeCodeExist(String capitalAssetTypeCode) {
Map<String, Object> pKeys = new HashMap<String, Object>();
pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAPITAL_ASSET_TYPE_CODE, capitalAssetTypeCode);
AssetType assetType = this.getBusinessObjectService().findByPrimaryKey(AssetType.class, pKeys);
return ObjectUtils.isNotNull(assetType);
}
/**
* Get PurAp PurchaseOrderCapitalAssetSystem Object if exists.
*
* @param capitalAssetSystemIdentifier
* @return
*/
protected PurchaseOrderCapitalAssetSystem findCapitalAssetSystem(Integer capitalAssetSystemIdentifier) {
Map pKeys = new HashMap<String, Object>();
pKeys.put(PurapPropertyConstants.CAPITAL_ASSET_SYSTEM_IDENTIFIER, capitalAssetSystemIdentifier);
return businessObjectService.findByPrimaryKey(PurchaseOrderCapitalAssetSystem.class, pKeys);
}
/**
* Set Asset Global org owner account and chart code. It's assigned by selecting the account that contributed the most dollars
* on the payment request.
*
* @param assetGlobal
*/
protected void setAssetGlobalOrgOwnerAccount(AssetGlobal assetGlobal) {
AssetPaymentDetail maxCostPayment = null;
// get the maximum payment cost
for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) {
if (maxCostPayment == null || assetPaymentDetail.getAmount().isGreaterThan(maxCostPayment.getAmount())) {
maxCostPayment = assetPaymentDetail;
}
}
if (maxCostPayment != null) {
assetGlobal.setOrganizationOwnerAccountNumber(maxCostPayment.getAccountNumber());
assetGlobal.setOrganizationOwnerChartOfAccountsCode(maxCostPayment.getChartOfAccountsCode());
}
}
/**
* Set Asset Global total cost amount.
*
* @param assetGlobal
*/
protected void setAssetGlobalTotalCost(AssetGlobal assetGlobal) {
KualiDecimal totalCost = KualiDecimal.ZERO;
for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) {
totalCost = totalCost.add(assetPaymentDetail.getAmount());
}
assetGlobal.setTotalCostAmount(totalCost);
}
/**
* Feeding data from preTag and set into asset global for shared information. PreTag data may override PurAp asset data since
* the strategy choose to respect Pretagging
*
* @param preTag
* @param assetGlobal
*/
protected void setAssetGlobalFromPreTag(Pretag preTag, AssetGlobal assetGlobal) {
if (StringUtils.isNotBlank(preTag.getManufacturerName())) {
assetGlobal.setManufacturerName(preTag.getManufacturerName());
}
if (StringUtils.isNotBlank(preTag.getManufacturerModelNumber())) {
assetGlobal.setManufacturerModelNumber(preTag.getManufacturerModelNumber());
}
if (StringUtils.isNotBlank(preTag.getCapitalAssetTypeCode())) {
assetGlobal.setCapitalAssetTypeCode(preTag.getCapitalAssetTypeCode());
}
assetGlobal.setOrganizationText(preTag.getOrganizationText());
assetGlobal.setRepresentativeUniversalIdentifier(preTag.getRepresentativeUniversalIdentifier());
if (StringUtils.isNotBlank(preTag.getVendorName())) {
assetGlobal.setVendorName(preTag.getVendorName());
}
// acquisition type code is set to "P"(Pre-asset tagging)
assetGlobal.setAcquisitionTypeCode(CamsConstants.AssetGlobal.PRE_TAGGING_ACQUISITION_TYPE_CODE);
}
/**
* Gets the businessObjectService attribute.
*
* @return Returns the businessObjectService.
*/
public BusinessObjectService getBusinessObjectService() {
return businessObjectService;
}
/**
* Sets the businessObjectService attribute value.
*
* @param businessObjectService The businessObjectService to set.
*/
public void setBusinessObjectService(BusinessObjectService businessObjectService) {
this.businessObjectService = businessObjectService;
}
/**
* Gets the documentService attribute.
*
* @return Returns the documentService.
*/
public DocumentService getDocumentService() {
return documentService;
}
/**
* Sets the documentService attribute value.
*
* @param documentService The documentService to set.
*/
public void setDocumentService(DocumentService documentService) {
this.documentService = documentService;
}
/**
* Gets the purApLineService attribute.
*
* @return Returns the purApLineService.
*/
public PurApLineService getPurApLineService() {
return purApLineService;
}
/**
* Sets the purApLineService attribute value.
*
* @param purApLineService The purApLineService to set.
*/
public void setPurApLineService(PurApLineService purApLineService) {
this.purApLineService = purApLineService;
}
/**
* Gets the purApInfoService attribute.
*
* @return Returns the purApInfoService.
*/
public PurApInfoService getPurApInfoService() {
return purApInfoService;
}
/**
* Sets the purApInfoService attribute value.
*
* @param purApInfoService The purApInfoService to set.
*/
public void setPurApInfoService(PurApInfoService purApInfoService) {
this.purApInfoService = purApInfoService;
}
private AssetGlobalService getAssetGlobalService() {
return assetGlobalService;
}
public void setAssetGlobalService(AssetGlobalService assetGlobalService) {
this.assetGlobalService = assetGlobalService;
}
}