/*
* 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.businessobject.lookup;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
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.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableProcessingReport;
import org.kuali.kfs.module.cab.service.PurchasingAccountsPayableReportService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.services.IdentityManagementService;
import org.kuali.rice.kns.lookup.HtmlData;
import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData;
import org.kuali.rice.kns.lookup.KualiLookupableHelperServiceImpl;
import org.kuali.rice.kns.lookup.LookupUtils;
import org.kuali.rice.krad.bo.BusinessObject;
import org.kuali.rice.krad.lookup.CollectionIncomplete;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.krad.util.UrlFactory;
/**
* This class overrids the base getActionUrls method
*/
public class PurApReportLookupableHelperServiceImpl extends KualiLookupableHelperServiceImpl {
private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurApReportLookupableHelperServiceImpl.class);
private PurchasingAccountsPayableReportService purApReportService;
/**
* Custom action urls for CAB PurAp lines.
*
* @see org.kuali.rice.kns.lookup.LookupableHelperService#getCustomActionUrls(org.kuali.rice.krad.bo.BusinessObject,
* java.util.List, java.util.List pkNames)
*/
@Override
public List<HtmlData> getCustomActionUrls(BusinessObject bo, List pkNames) {
Map<String,String> permissionDetails = new HashMap<String,String>();
permissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, "KFS-CAB");
permissionDetails.put(KimConstants.AttributeConstants.ACTION_CLASS, "PurApLineAction");
if (!SpringContext.getBean(IdentityManagementService.class).isAuthorizedByTemplateName(GlobalVariables.getUserSession().getPrincipalId(), KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.USE_SCREEN, permissionDetails, null)) {
return super.getEmptyActionUrls();
}
GeneralLedgerEntry glEntry = (GeneralLedgerEntry) bo;
Properties parameters = new Properties();
parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, CabConstants.Actions.START);
if (glEntry.getReferenceFinancialDocumentNumber() != null) {
parameters.put(CabPropertyConstants.PurchasingAccountsPayableDocument.PURCHASE_ORDER_IDENTIFIER, glEntry.getReferenceFinancialDocumentNumber());
}
String href = UrlFactory.parameterizeUrl(CabConstants.CB_INVOICE_LINE_ACTION_URL, parameters);
List<HtmlData> anchorHtmlDataList = new ArrayList<HtmlData>();
AnchorHtmlData anchorHtmlData = new AnchorHtmlData(href, CabConstants.Actions.START, CabConstants.ActivityStatusCode.PROCESSED_IN_CAMS.equalsIgnoreCase(glEntry.getActivityStatusCode()) ? CabConstants.Actions.VIEW : CabConstants.Actions.PROCESS);
anchorHtmlData.setTarget(KFSConstants.NEW_WINDOW_URL_TARGET);
anchorHtmlDataList.add(anchorHtmlData);
return anchorHtmlDataList;
}
/**
* @see org.kuali.rice.kns.lookup.KualiLookupableHelperServiceImpl#getSearchResults(java.util.Map)
*/
@Override
public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
setBackLocation(fieldValues.get(KFSConstants.BACK_LOCATION));
setDocFormKey(fieldValues.get(KFSConstants.DOC_FORM_KEY));
// purapDocumentIdentifier should query PurchasingAccountsPayableDocument
String purapDocumentIdentifier = getSelectedField(fieldValues, CabPropertyConstants.PurchasingAccountsPayableProcessingReport.PURAP_DOCUMENT_IDENTIFIER);
// Get the user selects 'Y' for "processed by CAMs". We will search for all status GL lines. This is because of the partial
// submitted GL lines when GL is 'N'(new) or 'M'(modified), partial GL lines could submit to CAMs. we should include these
// lines into the search result.
String active = getSelectedField(fieldValues, CabPropertyConstants.GeneralLedgerEntry.ACTIVITY_STATUS_CODE);
if (KFSConstants.ACTIVE_INDICATOR.equalsIgnoreCase(active)) {
fieldValues.remove(CabPropertyConstants.GeneralLedgerEntry.ACTIVITY_STATUS_CODE);
}
// search for GeneralLedgerEntry BO.
Iterator searchResultIterator = purApReportService.findGeneralLedgers(fieldValues);
// create PurchasingAccountsPayableProcessingReport search result collection.
List<PurchasingAccountsPayableProcessingReport> purApReportList = buildGlEntrySearchResultCollection(searchResultIterator, active);
// purapDocumentIdentifier is the attribute in PurchasingAccountsPayableDocument. We need to generate a new lookup for that
// BO, then join search results with the generalLedgerCollection to get the correct search result collection.
if (StringUtils.isNotBlank(purapDocumentIdentifier)) {
// construct the lookup criteria for PurchasingAccountsPayableDocument from user input
Map<String, String> purapDocumentLookupFields = getPurApDocumentLookupFields(fieldValues, purapDocumentIdentifier);
Collection purApDocs = purApReportService.findPurchasingAccountsPayableDocuments(purapDocumentLookupFields);
Map<String, Integer> purApDocNumberMap = buildDocumentNumberMap(purApDocs);
purApReportList = updatePurApReportListByPurApDocs(purApReportList, purApDocNumberMap);
}
else {
purApReportList = updateResultList(purApReportList);
}
return buildSearchResultList(purApReportList);
}
/**
* Get PurapDocumentIdentifier from PurchasingAccountsPayableDocument and add it to the search result.
*
* @param purApReportCollection
*/
private List<PurchasingAccountsPayableProcessingReport> updateResultList(List<PurchasingAccountsPayableProcessingReport> purApReportList) {
List<PurchasingAccountsPayableProcessingReport> newResultList = new ArrayList<PurchasingAccountsPayableProcessingReport>();
BusinessObjectService boService = this.getBusinessObjectService();
Map pKeys = new HashMap<String, String>();
for (PurchasingAccountsPayableProcessingReport report : purApReportList) {
pKeys.put(CabPropertyConstants.PurchasingAccountsPayableDocument.DOCUMENT_NUMBER, report.getDocumentNumber());
PurchasingAccountsPayableDocument purApDocument = boService.findByPrimaryKey(PurchasingAccountsPayableDocument.class, pKeys);
if (ObjectUtils.isNotNull(purApDocument)) {
report.setPurapDocumentIdentifier(purApDocument.getPurapDocumentIdentifier());
newResultList.add(report);
}
pKeys.clear();
}
return newResultList;
}
/**
* Build the search result list.
*
* @param purApReportCollection
* @return
*/
private List<? extends BusinessObject> buildSearchResultList(List purApReportList) {
List<? extends BusinessObject> searchResults = null;
Integer searchResultsLimit = LookupUtils.getSearchResultsLimit(PurchasingAccountsPayableProcessingReport.class);
Long matchingResultsCount = Long.valueOf(purApReportList.size());
// apply results set limit if necessary
if (searchResultsLimit > 0 && matchingResultsCount.intValue() > searchResultsLimit.intValue()) {
searchResults = new CollectionIncomplete(purApReportList.subList(0, searchResultsLimit), matchingResultsCount);
} else {
searchResults = new CollectionIncomplete(purApReportList, 0L);
}
return searchResults;
}
/**
* Build a HashMap for documentNumbers from the PurchasingAccountsPayableDocument search results
*
* @param purApDocs
* @return
*/
private Map<String, Integer> buildDocumentNumberMap(Collection purApDocs) {
Map<String, Integer> purApDocNumbers = new HashMap<String, Integer>();
for (Iterator iterator = purApDocs.iterator(); iterator.hasNext();) {
PurchasingAccountsPayableDocument purApdoc = (PurchasingAccountsPayableDocument) iterator.next();
purApDocNumbers.put(purApdoc.getDocumentNumber(), purApdoc.getPurapDocumentIdentifier());
}
return purApDocNumbers;
}
/**
* Build lookup fields for PurchasingAccountsPayableDocument lookup.
*
* @param fieldValues
* @param purapDocumentIdentifier
* @return
*/
private Map<String, String> getPurApDocumentLookupFields(Map<String, String> fieldValues, String purapDocumentIdentifier) {
Map<String, String> purapDocumentLookupFields = new HashMap<String, String>();
purapDocumentLookupFields.put(CabPropertyConstants.PurchasingAccountsPayableDocument.PURAP_DOCUMENT_IDENTIFIER, purapDocumentIdentifier);
purapDocumentLookupFields.put(CabPropertyConstants.PurchasingAccountsPayableDocument.DOCUMENT_NUMBER, fieldValues.get(CabPropertyConstants.GeneralLedgerEntry.DOCUMENT_NUMBER));
purapDocumentLookupFields.put(CabPropertyConstants.PurchasingAccountsPayableDocument.PURCHASE_ORDER_IDENTIFIER, fieldValues.get(CabPropertyConstants.GeneralLedgerEntry.REFERENCE_FINANCIAL_DOCUMENT_NUMBER));
return purapDocumentLookupFields;
}
/**
* Join PurapReportCollection and PurApDocsCollection by documentNumber and remove from PurapReportCollection for mismatch.
*
* @param purApReportCollection
* @param purApDocNumbers
*/
private List<PurchasingAccountsPayableProcessingReport> updatePurApReportListByPurApDocs(List<PurchasingAccountsPayableProcessingReport> purApReportList, Map<String, Integer> purApDocNumberMap) {
List<PurchasingAccountsPayableProcessingReport> newReportsList = new ArrayList<PurchasingAccountsPayableProcessingReport>();
for (PurchasingAccountsPayableProcessingReport report : purApReportList) {
if (purApDocNumberMap.containsKey(report.getDocumentNumber())) {
report.setPurapDocumentIdentifier(purApDocNumberMap.get(report.getDocumentNumber()));
newReportsList.add(report);
}
}
// remove from report collection
return newReportsList;
}
/**
* Build search result collection as PurchasingAccountsPayableProcessingReport collection.
*
* @param searchResultIterator
* @return
*/
private List<PurchasingAccountsPayableProcessingReport> buildGlEntrySearchResultCollection(Iterator searchResultIterator, String activeSelection) {
List<PurchasingAccountsPayableProcessingReport> purApReportList = new ArrayList();
while (searchResultIterator.hasNext()) {
Object glEntry = searchResultIterator.next();
if (glEntry.getClass().isArray()) {
int i = 0;
Object[] columnValues = (Object[]) glEntry;
PurchasingAccountsPayableProcessingReport newReport = new PurchasingAccountsPayableProcessingReport();
newReport.setUniversityFiscalYear(new Integer(columnValues[i++].toString()));
newReport.setUniversityFiscalPeriodCode(columnValues[i++].toString());
newReport.setChartOfAccountsCode(columnValues[i++].toString());
newReport.setAccountNumber(columnValues[i++].toString());
newReport.setFinancialObjectCode(columnValues[i++].toString());
newReport.setFinancialDocumentTypeCode(columnValues[i++].toString());
newReport.setDocumentNumber(columnValues[i++].toString());
newReport.setTransactionDebitCreditCode(columnValues[i] == null ? null : columnValues[i].toString());
i++;
newReport.setTransactionLedgerEntryAmount(columnValues[i] == null ? null : new KualiDecimal(columnValues[i].toString()));
i++;
newReport.setReferenceFinancialDocumentNumber(columnValues[i] == null ? null : columnValues[i].toString());
i++;
newReport.setTransactionDate(columnValues[i] == null ? null : getDate(columnValues[i]));
i++;
newReport.setTransactionLedgerSubmitAmount(columnValues[i] == null ? null : new KualiDecimal(columnValues[i].toString()));
i++;
newReport.setActivityStatusCode(columnValues[i] == null ? null : columnValues[i].toString());
if (!excludeFromSearchResults(newReport, activeSelection)) {
if (newReport.getActivityStatusCode() != null && newReport.isActive()) {
// adjust amount if the activity_status_code is 'N' or 'M'
if (newReport.getTransactionLedgerEntryAmount() != null) {
setReportAmount(activeSelection, newReport);
}
}
else {
// set report amount by transactional Amount
newReport.setReportAmount(newReport.getAmount());
}
purApReportList.add(newReport);
}
}
}
return purApReportList;
}
/**
* Get the Date instance. Why we need this? Looks OJB returns different type of instance when connect to MySql and Oracle:
* Oracle returns Date instance while MySql returns TimeStamp instance.
*
* @param obj
* @return
*/
private Date getDate(Object obj) {
if (obj instanceof Date) {
return (Date) obj;
}
else if (obj instanceof Timestamp) {
Timestamp tsp = (Timestamp) obj;
return new Date(tsp.getTime());
}
else {
return null;
}
}
/**
* To decide if the the given newReport should be added to the search result collection.
*
* @param newReport
* @param activeSelection
* @return
*/
private boolean excludeFromSearchResults(PurchasingAccountsPayableProcessingReport newReport, String activeSelection) {
// If the user selects processed by CAMs, we should exclude the GL lines which have no submit amount as the search result
if ((KFSConstants.ACTIVE_INDICATOR.equalsIgnoreCase(activeSelection) && (newReport.getTransactionLedgerSubmitAmount() == null || newReport.getTransactionLedgerSubmitAmount().isZero()))) {
return true;
}
return false;
}
/**
* set partial commit report amount
*
* @param active
* @param newReport
*/
private void setReportAmount(String active, PurchasingAccountsPayableProcessingReport newReport) {
if (KFSConstants.ACTIVE_INDICATOR.equalsIgnoreCase(active)) {
// Processed in CAMS: set report amount as submitted amount
newReport.setReportAmount(newReport.getTransactionLedgerSubmitAmount());
}
else if ((KFSConstants.NON_ACTIVE_INDICATOR.equalsIgnoreCase(active))) {
// Not Processed in CAMS: set report amount by transactionLedgerEntryAmount excluding submitted amount
KualiDecimal reportAmount = newReport.getAmount();
if (reportAmount != null && newReport.getTransactionLedgerSubmitAmount() != null) {
newReport.setReportAmount(reportAmount.subtract(newReport.getTransactionLedgerSubmitAmount()));
}
else {
newReport.setReportAmount(reportAmount);
}
}
else {
// both processed/non processed: set report amount by transactional amount
newReport.setReportAmount(newReport.getAmount());
}
}
/**
* Return and remove the selected field from the user input.
*
* @param fieldValues
* @param fieldName
* @return
*/
private String getSelectedField(Map fieldValues, String fieldName) {
String fieldValue = null;
if (fieldValues.containsKey(fieldName)) {
fieldValue = (String) fieldValues.get(fieldName);
}
return fieldValue == null ? "" : fieldValue;
}
/**
* Gets the purApReportService attribute.
*
* @return Returns the purApReportService.
*/
public PurchasingAccountsPayableReportService getPurApReportService() {
return purApReportService;
}
/**
* Sets the purApReportService attribute value.
*
* @param purApReportService The purApReportService to set.
*/
public void setPurApReportService(PurchasingAccountsPayableReportService purApReportService) {
this.purApReportService = purApReportService;
}
@Override
public BusinessObjectService getBusinessObjectService() {
return SpringContext.getBean(BusinessObjectService.class);
}
}