/*
* 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.tem.document.authorization;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.TemConstants.TravelDocTypes;
import org.kuali.kfs.module.tem.TemConstants.TravelReimbursementStatusCodeKeys;
import org.kuali.kfs.module.tem.TemKeyConstants;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.TemWorkflowConstants;
import org.kuali.kfs.module.tem.document.TravelAuthorizationDocument;
import org.kuali.kfs.module.tem.document.TravelReimbursementDocument;
import org.kuali.kfs.module.tem.document.service.TravelDocumentService;
import org.kuali.kfs.module.tem.document.service.TravelReimbursementService;
import org.kuali.kfs.module.tem.document.web.struts.TravelReimbursementForm;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kns.util.KNSGlobalVariables;
import org.kuali.rice.kns.web.struts.form.KualiForm;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Travel Reimbursement Document Presentation Controller
*
*/
public class TravelReimbursementDocumentPresentationController extends TravelDocumentPresentationController {
public static Logger LOG = Logger.getLogger(TravelReimbursementDocumentPresentationController.class);
protected volatile static BusinessObjectService businessObjectService;
/**
* @see org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationControllerBase#getEditModes(org.kuali.rice.kns.document.Document)
*/
@Override
public Set<String> getEditModes(Document document) {
Set<String> editModes = super.getEditModes(document);
addFullEntryEditMode(document, editModes);
editModes.remove(TemConstants.EditModes.CHECK_AMOUNT_ENTRY); // the check amount cannot be edited on travel reimbursements
editModes.remove(TemConstants.EditModes.BLANKET_TRAVEL_ENTRY);
editModes.remove(TemConstants.EditModes.BLANKET_TRAVEL_VIEW);
final Set<String> nodeNames = document.getDocumentHeader().getWorkflowDocument().getNodeNames();
if (document.getDocumentHeader().getWorkflowDocument().isInitiated() || document.getDocumentHeader().getWorkflowDocument().isSaved() || (nodeNames != null && !nodeNames.isEmpty() && (nodeNames.contains(TemWorkflowConstants.RouteNodeNames.TAX) || nodeNames.contains(TemWorkflowConstants.RouteNodeNames.AP_TRAVEL)))) {
editModes.add(TemConstants.EditModes.EXPENSE_TAXABLE_MODE);
}
if (document.getDocumentHeader().getWorkflowDocument().isInitiated() || document.getDocumentHeader().getWorkflowDocument().isSaved() || (nodeNames != null && !nodeNames.isEmpty() && nodeNames.contains(TemWorkflowConstants.RouteNodeNames.AP_TRAVEL))) {
editModes.add(TemConstants.EditModes.EXPENSE_LIMIT_ENTRY);
}
return editModes;
}
/**
* @see org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationControllerBase#getDocumentActions(org.kuali.rice.kns.document.Document)
*/
@Override
public Set<String> getDocumentActions(Document document) {
TravelReimbursementDocument tr = (TravelReimbursementDocument)document;
TravelAuthorizationDocument ta = null;
ta = getTravelDocumentService().findCurrentTravelAuthorization(tr);
if (ta != null){
if(ta.getDelinquentAction() != null && ta.getDelinquentAction().equals(TemConstants.DELINQUENT_STOP) && !ta.getDelinquentTRException()){
throw new DocumentInitiationException(TemKeyConstants.ERROR_AUTHORIZATION_TR_DELINQUENT, new String[] { TravelDocTypes.TRAVEL_REIMBURSEMENT_DOCUMENT }, true);
}
}
Set<String> actions = super.getDocumentActions(document);
if (canNewReimbursement(tr)) {
actions.add(TemConstants.TravelAuthorizationActions.CAN_NEW_REIMBURSEMENT);
}
return actions;
}
/**
* Determines if a reimbursement can be initiated for this document. This is done for {@link TravelReimbursementDocument} instances
* that have a workflow document status of FINAL or PROCESSED and on documents that have a workflow
* App Doc Status of DEPT_APPROVED. Also checks if the person has permission to initiate the target documents.
* Will not show the new Reimbursement link if the TR already has a TR enroute.
* Will not show the new Reimbursement link if the TR is not the root document.
* Will not show the new Reimbursement link if a TA is required to initiate a TR.
* Will not show the new Reimbursement link if all the Trip Types require a TA to initiate a TR.
*
* @param document
* @return
*/
public boolean canNewReimbursement(TravelReimbursementDocument document) {
final String documentType = document.getDocumentTypeName();
final String appDocStatus = document.getApplicationDocumentStatus();
boolean statusCheck = (document.getDocumentHeader().getWorkflowDocument().isProcessed() || document.getDocumentHeader().getWorkflowDocument().isFinal())
&& (appDocStatus.equals(TravelReimbursementStatusCodeKeys.DEPT_APPROVED));
Person user = GlobalVariables.getUserSession().getPerson();
boolean hasInitAccess = false;
if (getTemRoleService().canAccessTravelDocument(document, user) && !ObjectUtils.isNull(document.getTraveler()) && document.getTemProfileId() != null && !ObjectUtils.isNull(document.getTemProfile())){
//check if user also can init other docs
hasInitAccess = user.getPrincipalId().equals(document.getTraveler().getPrincipalId()) || getTemRoleService().isTravelDocumentArrangerForProfile(documentType, user.getPrincipalId(), document.getTemProfileId()) || getTemRoleService().isTravelArranger(user, document.getTemProfile().getHomeDepartment() , document.getTemProfileId().toString(), documentType);
}
boolean checkRelatedDocs = true;
boolean originalDocumentCheck = true;
boolean initiateReimbursementWithoutAuthorization = true;
if (documentType.equals(TravelDocTypes.TRAVEL_REIMBURSEMENT_DOCUMENT)) {
//don't display link if there are enroute TRs
List<Document> docs = getTravelDocumentService().getDocumentsRelatedTo(document, documentType);
for (Document doc : docs) {
TravelReimbursementDocument trDoc = (TravelReimbursementDocument)doc;
if (trDoc.getDocumentHeader().getWorkflowDocument().isEnroute()) {
checkRelatedDocs &= false;
}
}
//only allow the link to display if the TR is the root document
originalDocumentCheck = document.isTripProgenitor();
//only allow the link to display if a TR can be initiated without a TA
initiateReimbursementWithoutAuthorization = getConfigurationService().getPropertyValueAsBoolean(TemKeyConstants.CONFIG_PROPERTY_REIMBURSEMENT_INITIATELINK_ENABLED);
//check Trip Types to verify at least one type can initiate TR without TA
initiateReimbursementWithoutAuthorization &= !getTravelReimbursementService().doAllReimbursementTripTypesRequireTravelAuthorization();
}
return statusCheck && hasInitAccess && checkRelatedDocs && originalDocumentCheck && initiateReimbursementWithoutAuthorization;
}
/**
* @see org.kuali.rice.krad.document.DocumentPresentationControllerBase#canInitiate(java.lang.String)
*/
@Override
public boolean canInitiate(String documentTypeName) {
//only allow if a TR can be initiated without a TA
boolean initiateReimbursementWithoutAuthorization = getConfigurationService().getPropertyValueAsBoolean(TemKeyConstants.CONFIG_PROPERTY_REIMBURSEMENT_INITIATELINK_ENABLED);
//check Trip Types to verify at least one type can initiate TR without TA
initiateReimbursementWithoutAuthorization &= !getTravelReimbursementService().doAllReimbursementTripTypesRequireTravelAuthorization();
if (!initiateReimbursementWithoutAuthorization) {
throw new DocumentInitiationException(TemKeyConstants.ERROR_TA_REQUIRED_FOR_TR_INIT,new String[] {},true);
}
KualiForm form = KNSGlobalVariables.getKualiForm();
if (form instanceof TravelReimbursementForm) {
final TravelReimbursementForm reimbForm = (TravelReimbursementForm)form;
if (!StringUtils.isBlank(reimbForm.getTravelDocumentIdentifier())) {
// we're basing this document off of another document; let's look for any other TR's in the trip to verify they are not enroute
final List<TravelReimbursementDocument> trDocsInTrip = getTravelReimbursementsInTrip(reimbForm.getTravelDocumentIdentifier());
if (!trDocsInTrip.isEmpty()) {
for (TravelReimbursementDocument trDoc : trDocsInTrip) {
if (StringUtils.equals(trDoc.getFinancialSystemDocumentHeader().getFinancialDocumentStatusCode(), KFSConstants.DocumentStatusCodes.ENROUTE)) {
throw new DocumentInitiationException(TemKeyConstants.ERROR_TR_ENROUTE_DURING_TR_INIT, new String[] {reimbForm.getTravelDocumentIdentifier(), trDoc.getDocumentNumber()}, true);
}
}
}
}
}
return super.canInitiate(documentTypeName);
}
/**
* Look up any TravelReimbursementDocuments associated with the given trip
* @param tripId the travel document identifier for the trip we're thinking about initiating a new TR for
* @return a List of any existing TravelReimbursementDocuments in that trip
*/
protected List<TravelReimbursementDocument> getTravelReimbursementsInTrip(String tripId) {
Map<String, Object> fieldValues = new HashMap<String, Object>();
fieldValues.put(TemPropertyConstants.TRAVEL_DOCUMENT_IDENTIFIER, tripId);
Collection<TravelReimbursementDocument> trDocs = getBusinessObjectService().findMatching(TravelReimbursementDocument.class, fieldValues);
List<TravelReimbursementDocument> trDocsList = new ArrayList<TravelReimbursementDocument>();
if (!trDocs.isEmpty()) {
trDocsList.addAll(trDocs);
}
return trDocsList;
}
/**
* @see org.kuali.rice.krad.document.DocumentPresentationControllerBase#canCopy(org.kuali.rice.krad.document.Document)
*/
@Override
public boolean canCopy(Document document) {
boolean canCopy = super.canCopy(document);
//1. TR has been saved
if (canCopy) {
canCopy = !isDocumentInitiated(document);
}
//2. TR was not created from a TA
if (canCopy) {
canCopy = !isReimbursementChildOfAuthorization(document);
}
return canCopy;
}
protected boolean isDocumentInitiated(Document document) {
return document.getDocumentHeader().getWorkflowDocument().isInitiated();
}
protected boolean isReimbursementChildOfAuthorization(Document document) {
TravelReimbursementDocument tr = (TravelReimbursementDocument)document;
List<TravelAuthorizationDocument> travelAuthorizations = getTravelDocumentService().findAuthorizationDocuments(tr.getTravelDocumentIdentifier());
if (ObjectUtils.isNotNull(travelAuthorizations) && !travelAuthorizations.isEmpty()) {
return true;
}
else {
return false;
}
}
@Override
protected TravelDocumentService getTravelDocumentService() {
return SpringContext.getBean(TravelDocumentService.class);
}
protected TravelReimbursementService getTravelReimbursementService() {
return SpringContext.getBean(TravelReimbursementService.class);
}
protected BusinessObjectService getBusinessObjectService() {
if (businessObjectService == null) {
businessObjectService = SpringContext.getBean(BusinessObjectService.class);
}
return businessObjectService;
}
}