/* * 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.web.struts; import static org.apache.commons.lang.StringUtils.isNotBlank; import static org.apache.commons.lang.StringUtils.substringBetween; import static org.kuali.kfs.module.tem.TemConstants.CERTIFICATION_STATEMENT_ATTRIBUTE; import static org.kuali.kfs.module.tem.TemConstants.COVERSHEET_FILENAME_FORMAT; import static org.kuali.kfs.module.tem.TemConstants.EMPLOYEE_TEST_ATTRIBUTE; import static org.kuali.kfs.module.tem.TemConstants.EXPENSE_SUMMARY_REPORT_TITLE; import static org.kuali.kfs.module.tem.TemConstants.REMAINING_DISTRIBUTION_ATTRIBUTE; import static org.kuali.kfs.module.tem.TemConstants.SHOW_ACCOUNT_DISTRIBUTION_ATTRIBUTE; import static org.kuali.kfs.module.tem.TemConstants.SHOW_ADVANCES_ATTRIBUTE; import static org.kuali.kfs.module.tem.TemConstants.SHOW_ENCUMBRANCE_ATTRIBUTE; import static org.kuali.kfs.module.tem.TemConstants.SHOW_REPORTS_ATTRIBUTE; import static org.kuali.kfs.module.tem.TemConstants.SUMMARY_BY_DAY_TITLE; import static org.kuali.kfs.module.tem.TemConstants.TravelReimbursementParameters.DISPLAY_ACCOUNTING_DISTRIBUTION_TAB_IND; import static org.kuali.kfs.module.tem.TemConstants.TravelReimbursementParameters.DISPLAY_ADVANCES_IN_REIMBURSEMENT_TOTAL_IND; import static org.kuali.kfs.module.tem.TemConstants.TravelReimbursementParameters.DISPLAY_ENCUMBRANCE_IND; import static org.kuali.kfs.sys.KFSPropertyConstants.DOCUMENT_NUMBER; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.kuali.kfs.module.tem.TemConstants; import org.kuali.kfs.module.tem.TemPropertyConstants; import org.kuali.kfs.module.tem.businessobject.AccountingDistribution; import org.kuali.kfs.module.tem.businessobject.AccountingDocumentRelationship; import org.kuali.kfs.module.tem.businessobject.ActualExpense; import org.kuali.kfs.module.tem.businessobject.PerDiemExpense; import org.kuali.kfs.module.tem.businessobject.SpecialCircumstances; import org.kuali.kfs.module.tem.document.TravelAuthorizationCloseDocument; import org.kuali.kfs.module.tem.document.TravelAuthorizationDocument; import org.kuali.kfs.module.tem.document.TravelDocument; import org.kuali.kfs.module.tem.document.TravelDocumentBase; import org.kuali.kfs.module.tem.document.TravelReimbursementDocument; import org.kuali.kfs.module.tem.document.authorization.TravelReimbursementAuthorizer; import org.kuali.kfs.module.tem.document.service.TravelAuthorizationService; import org.kuali.kfs.module.tem.document.service.TravelReimbursementService; import org.kuali.kfs.module.tem.document.web.bean.TravelReimbursementMvcWrapperBean; import org.kuali.kfs.module.tem.pdf.Coversheet; import org.kuali.kfs.module.tem.report.ExpenseSummaryReport; import org.kuali.kfs.module.tem.report.NonEmployeeCertificationReport; import org.kuali.kfs.module.tem.report.SummaryByDayReport; import org.kuali.kfs.module.tem.report.service.ExpenseSummaryReportService; import org.kuali.kfs.module.tem.report.service.NonEmployeeCertificationReportService; import org.kuali.kfs.module.tem.report.service.SummaryByDayReportService; import org.kuali.kfs.module.tem.report.util.BarcodeHelper; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.businessobject.AccountingLine; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.kew.api.exception.WorkflowException; import org.kuali.rice.kns.util.WebUtils; import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.ObjectUtils; /*** * Action methods for the {@link TravelReimbursementDocument} * */ public class TravelReimbursementAction extends TravelActionBase { public static Logger LOG = Logger.getLogger(TravelReimbursementAction.class); /** * Refreshes all collections upon load * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase) */ @Override protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { super.loadDocument(kualiDocumentFormBase); final TravelReimbursementForm reimbForm = (TravelReimbursementForm) kualiDocumentFormBase; final TravelReimbursementDocument document = reimbForm.getTravelReimbursementDocument(); refreshCollectionsFor(document); initializeAssignAccounts(reimbForm); reimbForm.setDistribution(getAccountingDistributionService().buildDistributionFrom(document)); } protected void refreshCollectionsFor(final TravelReimbursementDocument reimbursement) { if (!reimbursement.getDocumentHeader().getWorkflowDocument().isInitiated()) { LOG.debug("Refreshing objects in reimbursement"); reimbursement.refreshReferenceObject(TemPropertyConstants.PER_DIEM_EXPENSES); reimbursement.refreshReferenceObject(TemPropertyConstants.TRAVELER); reimbursement.refreshReferenceObject(TemPropertyConstants.TRIP_TYPE); reimbursement.refreshReferenceObject(TemPropertyConstants.ACTUAL_EXPENSES); reimbursement.refreshReferenceObject(TemPropertyConstants.PRIMARY_DESTINATION); reimbursement.refreshReferenceObject(TemPropertyConstants.SPECIAL_CIRCUMSTANCES); } } /** * This method sets all the boolean properties on the form to determine what buttons can be displayed depending on what is going * on */ protected void setButtonPermissions(TravelReimbursementForm form) { canSave(form); setCanReturnToFisicalOfficer(form); setCanCertify(form); setCanCalculate(form); } protected void canSave(TravelReimbursementForm reqForm) { boolean can = !(isFinal(reqForm) || isProcessed(reqForm)); if (can) { TravelReimbursementAuthorizer documentAuthorizer = getDocumentAuthorizer(reqForm); can = documentAuthorizer.canSave(reqForm.getTravelDocument(), GlobalVariables.getUserSession().getPerson()); } if (!can){ TravelReimbursementDocument tr = (TravelReimbursementDocument) reqForm.getDocument(); boolean isTravelManager = getTravelDocumentService().isTravelManager(GlobalVariables.getUserSession().getPerson()); boolean isDelinquent = tr.getDelinquentAction() != null; if (isTravelManager && isDelinquent) { can = true; } } if (can) { reqForm.getDocumentActions().put(KRADConstants.KUALI_ACTION_CAN_SAVE,true); } else{ reqForm.getDocumentActions().remove(KRADConstants.KUALI_ACTION_CAN_SAVE); } } /** * Determines whether or not someone can certify a travel document * * @param authForm */ protected void setCanCertify(TravelReimbursementForm form) { final TravelReimbursementAuthorizer authorizer = getDocumentAuthorizer(form); //certify form.setCanCertify(authorizer.canCertify(form.getTravelReimbursementDocument(), GlobalVariables.getUserSession().getPerson())); } public ActionForward printCoversheet(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; final String documentNumber = request.getParameter(DOCUMENT_NUMBER); if(documentNumber != null && !documentNumber.isEmpty()) { reimbForm.setDocument(getTravelReimbursementService().find(documentNumber)); } final TravelReimbursementDocument reimbursement = reimbForm.getTravelReimbursementDocument(); final Coversheet cover = getTravelReimbursementService().generateCoversheetFor(reimbursement); final ByteArrayOutputStream stream = new ByteArrayOutputStream(); cover.print(stream); WebUtils.saveMimeOutputStreamAsFile(response, "application/pdf", stream, String.format(COVERSHEET_FILENAME_FORMAT, reimbursement.getTravelDocumentIdentifier())); return null; } /** * Action method for creating a {@link ExpenseSummaryReport} and producing a PDF from it */ public ActionForward viewExpenseSummary(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; reimbForm.setDocument(getTravelReimbursementService().find(request.getParameter(DOCUMENT_NUMBER))); final TravelReimbursementDocument reimbursement = reimbForm.getTravelReimbursementDocument(); final ExpenseSummaryReport report = getExpenseSummaryReportService().buildReport(reimbursement); final ByteArrayOutputStream baos = getTravelReportService().buildReport(report); WebUtils.saveMimeOutputStreamAsFile(response, "application/pdf", baos, String.format(EXPENSE_SUMMARY_REPORT_TITLE, reimbursement.getTravelDocumentIdentifier())); return null; } /** * Action method for creating a {@link SummaryByDayReport} and producing a PDF from it. */ public ActionForward viewSummaryByDay(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; reimbForm.setDocument(getTravelReimbursementService().find(request.getParameter(DOCUMENT_NUMBER))); final TravelReimbursementDocument reimbursement = reimbForm.getTravelReimbursementDocument(); final SummaryByDayReport report = getSummaryByDayReportService().buildReport(reimbursement); final ByteArrayOutputStream baos = getTravelReportService().buildReport(report); WebUtils.saveMimeOutputStreamAsFile(response, "application/pdf", baos, String.format(SUMMARY_BY_DAY_TITLE, reimbursement.getTravelDocumentIdentifier())); return null; } /** * Action method for creating a {@link NonEmployeeCertificationReport} and producing a PDF from it. */ public ActionForward viewNonEmployeeForms(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; reimbForm.setDocument(getTravelReimbursementService().find(request.getParameter(DOCUMENT_NUMBER))); final TravelReimbursementDocument reimbursement = reimbForm.getTravelReimbursementDocument(); final NonEmployeeCertificationReport report = getNonEmployeeCertificationReportService().buildReport(reimbursement); BarcodeHelper barcode = new BarcodeHelper(); report.setBarcodeImage(barcode.generateBarcodeImage(reimbursement.getDocumentNumber())); File reportFile = getNonEmployeeCertificationReportService().generateReport(report); StringBuilder fileName = new StringBuilder(); fileName.append(reimbursement.getTravelDocumentIdentifier()); fileName.append(TemConstants.NON_EMPLOYEE_CERTIFICATION_REPORT_TITLE); fileName.append(KFSConstants.ReportGeneration.PDF_FILE_EXTENSION); if (reportFile.length() == 0) { return mapping.findForward(KFSConstants.MAPPING_BASIC); } displayPDF(request, response, reportFile, fileName); return null; } protected void initializePerDiem(final TravelReimbursementDocument reimbursement, final TravelAuthorizationDocument authorization) { for (final PerDiemExpense estimate : authorization.getPerDiemExpenses()) { final PerDiemExpense mileage = getTravelDocumentService().copyPerDiemExpense(estimate); mileage.setDocumentNumber(reimbursement.getDocumentNumber()); if (!StringUtils.isBlank(mileage.getMileageRateExpenseTypeCode())) { LOG.debug("Adding mileage for estimate with date "+ estimate.getMileageDate()); reimbursement.getPerDiemExpenses().add(mileage); } } } /** * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; ActionForward actionAfterPrimaryDestinationLookup = refreshAfterPrimaryDestinationLookup(mapping, reimbForm, request); if (actionAfterPrimaryDestinationLookup != null) { return actionAfterPrimaryDestinationLookup; } return super.refresh(mapping, form, request, response); } @Override protected void performRequesterRefresh(TravelDocument document, TravelFormBase travelForm, HttpServletRequest request) { final String travelerTypeCode = request.getParameter("document.traveler.travelerTypeCode"); if (StringUtils.isNotEmpty(travelerTypeCode)) { document.getTraveler().setTravelerTypeCode(travelerTypeCode); } document.getTraveler().refreshReferenceObject(TemPropertyConstants.TRAVELER_TYPE); ((TravelReimbursementDocument)document).updatePayeeTypeForReimbursable(); updateAccountsWithNewProfile(travelForm, document.getTemProfile()); } protected Integer getPerDiemActionLineNumber(final HttpServletRequest request) { for (final String parameterKey : ((Map<String,String>) request.getParameterMap()).keySet()) { if (StringUtils.containsIgnoreCase(parameterKey, TemPropertyConstants.PER_DIEM_EXPENSES)) { return getLineNumberFromParameter(parameterKey); } } return -1; } protected PerDiemExpense getPerDiemActionLine(final HttpServletRequest request, final TravelReimbursementDocument reimbursement) { final int lineNum = getPerDiemActionLineNumber(request); if (lineNum < 0) { return null; } return reimbursement.getPerDiemExpenses().get(lineNum); } /** * Uses the {@link TravelAuthorizationService} to lookup a {@link TravelAuthorizationDocument} instance via * its <code>travelDocumentIdentifier</code> * * @param travelDocumentIdentifier to location a {@link TravelAuthorizationDocument} with * @return {@link TravelAuthorizationDocument} instance */ protected TravelAuthorizationDocument getTravelAuthorization(final String travelDocumentIdentifier) { Collection<TravelAuthorizationDocument> taList = getTravelAuthorizationService().find(travelDocumentIdentifier); if (ObjectUtils.isNotNull(taList) && taList.iterator().hasNext()) { return taList.iterator().next(); } return null; } /** * Action method for adding an {@link OtherExpenseDetail} instance to the {@link OtherExpenseLine} * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward addOtherExpenseDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; final TravelReimbursementMvcWrapperBean mvcWrapper = newMvcDelegate(form); reimbForm.getObservable().notifyObservers(new Object[] { mvcWrapper, getSelectedLine(request) }); KualiDecimal totalRemaining = KualiDecimal.ZERO; for (final AccountingDistribution dist : reimbForm.getDistribution()) { totalRemaining = totalRemaining.add(dist.getRemainingAmount()); } request.setAttribute(REMAINING_DISTRIBUTION_ATTRIBUTE, totalRemaining); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * This method removes an other travel expense detail from this collection * * @param mapping * @param form * @param request * @param response * @return the page to forward back to * @throws Exception */ public ActionForward deleteOtherExpenseDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; final TravelReimbursementDocument document = (TravelReimbursementDocument) reimbForm.getDocument(); final TravelReimbursementMvcWrapperBean mvcWrapper = newMvcDelegate(form); reimbForm.getObservable().notifyObservers(new Object[] { mvcWrapper, getSelectedOtherExpenseIndex(request, document), getSelectedLine(request) }); return mapping.findForward(KFSConstants.MAPPING_BASIC); } @Override protected Class getMvcWrapperInterface() { return TravelReimbursementMvcWrapperBean.class; } /** * Do initialization for a new {@link TravelReimbursementDocument} * * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#createDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase) */ @Override protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { super.createDocument(kualiDocumentFormBase); final TravelReimbursementForm travelForm = (TravelReimbursementForm) kualiDocumentFormBase; final TravelReimbursementDocument document = (TravelReimbursementDocument) travelForm.getDocument(); getTravelReimbursementService().addListenersTo(document); document.addContactInformation(); if (!StringUtils.isBlank(travelForm.getTravelDocumentIdentifier())) { LOG.debug("Creating reimbursement for document number "+ travelForm.getTravelDocumentIdentifier()); document.setTravelDocumentIdentifier(travelForm.getTravelDocumentIdentifier()); TravelDocument rootDocument = getTravelDocumentService().findRootForTravelReimbursement(document.getTravelDocumentIdentifier()); if (ObjectUtils.isNull(rootDocument)) { String errorMsg = "Retrieved null TravelDocument when searching by travelDocumentIdentifier: "+ document.getTravelDocumentIdentifier() + " Cannot create a new document"; LOG.error(errorMsg); throw new RuntimeException(errorMsg); } LOG.debug("Setting traveler with id "+ rootDocument.getTravelerDetailId()); document.setTravelerDetailId(rootDocument.getTravelerDetailId()); document.refreshReferenceObject(TemPropertyConstants.TRAVELER); LOG.debug("Traveler is "+ document.getTraveler()+ " with customer number "+ document.getTraveler().getCustomerNumber()); if (document.getTraveler().getPrincipalId() != null) { document.getTraveler().setPrincipalName(getPersonService().getPerson(document.getTraveler().getPrincipalId()).getPrincipalName()); } document.updatePayeeTypeForReimbursable(); document.setPrimaryDestinationId(rootDocument.getPrimaryDestinationId()); document.setPrimaryDestination(rootDocument.getPrimaryDestination()); document.setTripDescription(rootDocument.getTripDescription()); document.setTripType(rootDocument.getTripType()); document.setTripTypeCode(rootDocument.getTripTypeCode()); document.setPrimaryDestination(rootDocument.getPrimaryDestination()); document.setTripBegin(rootDocument.getTripBegin()); document.setTripEnd(rootDocument.getTripEnd()); document.setPrimaryDestinationName(rootDocument.getPrimaryDestinationName()); document.setPrimaryDestinationCounty(rootDocument.getPrimaryDestinationCounty()); document.setPrimaryDestinationCountryState(rootDocument.getPrimaryDestinationCountryState()); document.setGroupTravelers(getTravelDocumentService().copyGroupTravelers(rootDocument.getGroupTravelers(), document.getDocumentNumber())); document.setDelinquentTRException(rootDocument.getDelinquentTRException()); document.setBlanketTravel(rootDocument.getBlanketTravel()); document.setMealWithoutLodgingReason(rootDocument.getMealWithoutLodgingReason()); document.configureTraveler(rootDocument.getTemProfileId(), rootDocument.getTraveler()); document.setExpenseLimit(rootDocument.getExpenseLimit()); document.setPerDiemAdjustment(rootDocument.getPerDiemAdjustment()); document.getDocumentHeader().setOrganizationDocumentNumber(rootDocument.getDocumentHeader().getOrganizationDocumentNumber()); if (document.getPrimaryDestinationId() != null && document.getPrimaryDestinationId().intValue() == TemConstants.CUSTOM_PRIMARY_DESTINATION_ID){ document.getPrimaryDestination().setPrimaryDestinationName(document.getPrimaryDestinationName()); document.getPrimaryDestination().setCounty(document.getPrimaryDestinationCounty()); document.getPrimaryDestination().getRegion().setRegionName(document.getPrimaryDestinationCountryState()); document.setPrimaryDestinationIndicator(true); } //copy special circumstances from root document for (SpecialCircumstances rootSpecialCircumstances : rootDocument.getSpecialCircumstances() ) { for (SpecialCircumstances circumstances : document.getSpecialCircumstances()) { if(circumstances.getQuestionId().equals(rootSpecialCircumstances.getQuestionId())) { circumstances.setText(rootSpecialCircumstances.getText()); } } } //only initialize per diem and copy expenses for a TR created from a TA if (rootDocument instanceof TravelAuthorizationDocument) { if (isCopyPerDiemAndExpenses(document)) { initializePerDiem(document, (TravelAuthorizationDocument)rootDocument); document.setActualExpenses((List<ActualExpense>) getTravelDocumentService().copyActualExpenses(rootDocument.getActualExpenses(), document.getDocumentNumber())); // add new detail for the copied actualExpenses if (document.getActualExpenses() != null && !document.getActualExpenses().isEmpty()) { for (int i = 0; i < document.getActualExpenses().size(); i++) { travelForm.getNewActualExpenseLines().add(new ActualExpense()); } } } } final AccountingDocumentRelationship relationship = buildRelationshipToProgenitorDocument(rootDocument, document); getBusinessObjectService().save(relationship); } else { // we have no parent document; blank out the trip begin and end dates document.setTripBegin(null); document.setTripEnd(null); document.setTripProgenitor(true); // this is the trip progenitor } // do the distribution travelForm.setDistribution(getAccountingDistributionService().buildDistributionFrom(travelForm.getTravelDocument())); initializeAssignAccounts(travelForm); } @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final ActionForward retval = super.execute(mapping, form, request, response); final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form; final TravelReimbursementDocument document = ((TravelReimbursementForm) form).getTravelReimbursementDocument(); final String travelIdentifier = document.getTravelDocumentIdentifier(); // should we refresh the trip type, upon which so much depends? let's check and do so if we need to if (!StringUtils.isBlank(document.getTripTypeCode())) { if (ObjectUtils.isNull(document.getTripType()) || !StringUtils.equals(document.getTripType().getCode(), document.getTripTypeCode())) { document.refreshReferenceObject(TemPropertyConstants.TRIP_TYPE); } } else { document.setTripType(null); } setButtonPermissions(reimbForm); LOG.debug("Found "+ document.getActualExpenses().size()+ " other expenses"); if (reimbForm.getHistory() == null) { LOG.debug("Looking up history for TEM document number "+ travelIdentifier); final List<Serializable> history = new ArrayList<Serializable>(); final Collection<TravelReimbursementDocument> docs = getTravelReimbursementService().findByTravelId(travelIdentifier); LOG.debug("Got history of size "+ docs.size()); for (final TravelReimbursementDocument found : docs) { LOG.debug("Creating history object for document "+ found); LOG.debug("Using header "+ found.getDocumentHeader()); history.add(new HistoryValueObject(found)); } reimbForm.setHistory(history); } //Set request variable that will determine whether to show the "Final Reimbursement" checkbox. //If TAC exists, no need to create another. TravelAuthorizationDocument authorization = getTravelDocumentService().findCurrentTravelAuthorization(document); if (authorization instanceof TravelAuthorizationCloseDocument){ request.setAttribute("isClose", true); } disablePerDiemExpenes(document); if(ObjectUtils.isNotNull(document.getActualExpenses())){ document.enableExpenseTypeSpecificFields(document.getActualExpenses()); } refreshRelatedDocuments(reimbForm); if (!reimbForm.getMethodToCall().equalsIgnoreCase("dochandler")) { if (document.getTripType() != null) { // Setting up distribution KualiDecimal totalRemaining = KualiDecimal.ZERO; for (final AccountingDistribution dist : reimbForm.getDistribution()) { totalRemaining = totalRemaining.add(dist.getRemainingAmount()); } request.setAttribute(REMAINING_DISTRIBUTION_ATTRIBUTE, totalRemaining); } } showAccountDistribution(request, document); request.setAttribute(SHOW_REPORTS_ATTRIBUTE, !document.getDocumentHeader().getWorkflowDocument().isInitiated()); request.setAttribute(CERTIFICATION_STATEMENT_ATTRIBUTE, getCertificationStatement(document)); request.setAttribute(EMPLOYEE_TEST_ATTRIBUTE, isEmployee(document.getTraveler())); request.setAttribute(TemConstants.DELINQUENT_TEST_ATTRIBUTE, document.getDelinquentAction()); LOG.debug("Found "+ document.getActualExpenses().size()+ " other expenses"); final boolean showAdvances = getParameterService().getParameterValueAsBoolean(TravelReimbursementDocument.class, DISPLAY_ADVANCES_IN_REIMBURSEMENT_TOTAL_IND); request.setAttribute(SHOW_ADVANCES_ATTRIBUTE, showAdvances); final boolean showEncumbrance = getParameterService().getParameterValueAsBoolean(TravelReimbursementDocument.class, DISPLAY_ENCUMBRANCE_IND); request.setAttribute(SHOW_ENCUMBRANCE_ATTRIBUTE, showEncumbrance); if(!getCalculateIgnoreList().contains(reimbForm.getMethodToCall())){ recalculateTripDetailTotalOnly(mapping, form, request, response); } getTravelDocumentService().showNoTravelAuthorizationError(document); final KualiDecimal paymentTotal = document.getPaymentAmount(); // the grand total is the amount that's actually reimbursable from this trip if (paymentTotal != null && !ObjectUtils.isNull(document.getTravelPayment()) && paymentTotal.isGreaterEqual(KualiDecimal.ZERO)) { document.getTravelPayment().setCheckTotalAmount(paymentTotal); } // and update the new source line if possible if (reimbForm.getNewSourceLine() != null) { final String objectCode = getObjectCodeForNewSourceAccountingLine(reimbForm); reimbForm.getNewSourceLine().setFinancialObjectCode(objectCode); } if (reimbForm.getAccountDistributionsourceAccountingLines() == null || reimbForm.getAccountDistributionsourceAccountingLines().isEmpty()) { initializeAssignAccounts(reimbForm); } return retval; } /** * The action called when the "Remove Per Diem Table" buttons are clicked upon. This method will clear out the per diem objects * from the {@link TravelAuthorizationDocument} instance * * @param mapping * @param form * @param request * @param response * @return {@link ActionForward} */ public ActionForward clearPerDiem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementDocument reimbursement = ((TravelReimbursementForm) form).getTravelReimbursementDocument(); reimbursement.setPerDiemExpenses(new ArrayList<PerDiemExpense>()); return mapping.findForward(KFSConstants.MAPPING_BASIC); } @Override public ActionForward clearPerDiemExpenses(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { TravelFormBase reqForm = (TravelFormBase) form; TravelDocument document = reqForm.getTravelDocument(); document.setPerDiemExpenses(new ArrayList<PerDiemExpense>()); getTravelReimbursementService().enableDuplicateExpenses((TravelReimbursementDocument) document, null); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Parses the method to call attribute to pick off the line number which should have an action performed on it. * * @param request * @param document the other expense is selected on * @return OtherExpense */ protected ActualExpense getSelectedOtherExpense(final HttpServletRequest request, final TravelReimbursementDocument document) { ActualExpense retval = null; final String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE); if (isNotBlank(parameterName)) { final int lineNumber = Integer.parseInt(substringBetween(parameterName, TemPropertyConstants.ACTUAL_EXPENSES+"[", "].")); retval = document.getActualExpenses().get(lineNumber); } return retval; } /** * This is a utility method used to prepare to and to return to a previous page, making sure that the buttons will be restored * in the process. * * @param kualiDocumentFormBase The Form, considered as a KualiDocumentFormBase, as it typically is here. * @return An actionForward mapping back to the original page. */ protected ActionForward returnToPreviousPage(ActionMapping mapping, KualiDocumentFormBase kualiDocumentFormBase) { return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Recalculates the Expenses Total Tab * * @param mapping * @param form * @param request * @param response * @throws Exception */ public ActionForward recalculate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { TravelFormBase travelReqForm = (TravelFormBase) form; TravelDocumentBase travelReqDoc = (TravelDocumentBase) travelReqForm.getDocument(); if (travelReqForm.getDocument() instanceof TravelReimbursementDocument) { final boolean showAdvances = getParameterService().getParameterValueAsBoolean(TravelReimbursementDocument.class, DISPLAY_ADVANCES_IN_REIMBURSEMENT_TOTAL_IND); request.setAttribute(SHOW_ADVANCES_ATTRIBUTE, showAdvances); } return recalculateTripDetailTotal(mapping, form, request, response); } @Override public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { getTravelDocumentService().showNoTravelAuthorizationError(((TravelReimbursementForm) form).getTravelReimbursementDocument()); return super.approve(mapping, form, request, response); } @Override public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { addAccountingDocumentRelationship(form); return super.save(mapping, form, request, response); } private void addAccountingDocumentRelationship(ActionForm form) throws WorkflowException { TravelReimbursementForm reqForm = (TravelReimbursementForm) form; TravelReimbursementDocument trDoc = reqForm.getTravelReimbursementDocument(); String travelDocumentIdentifier = trDoc.getTravelDocumentIdentifier(); if (ObjectUtils.isNotNull(travelDocumentIdentifier)) { TravelDocument rootDocument = getTravelDocumentService().findRootForTravelReimbursement(travelDocumentIdentifier); if (ObjectUtils.isNotNull(rootDocument)) { String relationshipDescription = rootDocument.getDocumentTypeName() +" - "+ trDoc.getDocumentTypeName(); getAccountingDocumentRelationshipService().save(new AccountingDocumentRelationship(rootDocument.getDocumentNumber(), trDoc.getDocumentNumber(), relationshipDescription)); } } } /** * Parses the method to call attribute to pick off the line number which should have an action performed on it. * * @param request * @param document the other expense is selected on * @return index of an OtherExpense */ protected int getSelectedOtherExpenseIndex(final HttpServletRequest request, final TravelReimbursementDocument document) { final String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE); LOG.debug("Getting selected other expense index from "+ parameterName); if (isNotBlank(parameterName)) { return Integer.parseInt(substringBetween(parameterName, TemPropertyConstants.ACTUAL_EXPENSES+"[", "].")); } return -1; } /** * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#route(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { getTravelDocumentService().showNoTravelAuthorizationError(((TravelReimbursementForm) form).getTravelReimbursementDocument()); final boolean showAccountDistribution = getParameterService().getParameterValueAsBoolean(TravelReimbursementDocument.class, DISPLAY_ACCOUNTING_DISTRIBUTION_TAB_IND); request.setAttribute(SHOW_ACCOUNT_DISTRIBUTION_ATTRIBUTE, showAccountDistribution); ActionForward forward = super.route(mapping, form, request, response); if (!StringUtils.isBlank(forward.getPath()) && forward.getPath().indexOf(KRADConstants.QUESTION_ACTION) < 0 ) { addDateChangedNote(form); addAccountingDocumentRelationship(form); } return forward; } /** * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#blanketApprove(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward blanketApprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { getTravelDocumentService().showNoTravelAuthorizationError(((TravelReimbursementForm) form).getTravelReimbursementDocument()); ActionForward forward = super.blanketApprove(mapping, form, request, response); if (!StringUtils.isBlank(forward.getPath()) && forward.getPath().indexOf(KRADConstants.QUESTION_ACTION) < 0 ) { addDateChangedNote(form); addAccountingDocumentRelationship(form); } return forward; } /** * This method calls addDateChangedNote() if this TR is created from a TA. * * @see org.kuali.kfs.module.tem.document.service.TravelReimbursementService#addDateChangedNote(org.kuali.kfs.module.tem.document.TravelReimbursementDocument, * org.kuali.kfs.module.tem.document.TravelAuthorizationDocument) * * @param form * @throws Exception */ protected void addDateChangedNote(ActionForm form) throws Exception { TravelReimbursementForm reqForm = (TravelReimbursementForm) form; TravelReimbursementDocument travelReqDoc = reqForm.getTravelReimbursementDocument(); String docId = travelReqDoc.getTravelDocumentIdentifier(); if (ObjectUtils.isNotNull(docId)) { TravelAuthorizationDocument taDoc = getTravelDocumentService().findCurrentTravelAuthorization(travelReqDoc); if (ObjectUtils.isNotNull(taDoc)) { getTravelReimbursementService().addDateChangedNote(travelReqDoc, taDoc); } } } /** * Determines the object code for the next source accounting line, based on the distribution for the document * @param form the reimbursement form * @return the object code to set on the new source accounting line */ protected String getObjectCodeForNewSourceAccountingLine(TravelReimbursementForm form) { if (form.getDistribution() != null && !form.getDistribution().isEmpty()) { if (form.getDistribution().size() == 1) { return form.getDistribution().get(0).getObjectCode(); } else { Set<String> nonUsedDistributionObjectCodes = new HashSet<String>(); Set<String> usedObjectCodes = getAccountingLineObjectCodes(form); for (AccountingDistribution dist : form.getDistribution()) { if (!usedObjectCodes.contains(dist.getObjectCode()) && !dist.getSubTotal().equals(KualiDecimal.ZERO)) { nonUsedDistributionObjectCodes.add(dist.getObjectCode()); } } if (nonUsedDistributionObjectCodes.size() == 1) { // only one left, let's set it; and we can use a for loop to grab the code because...obviously, it will only go once String objectCode = null; for (String objCode : nonUsedDistributionObjectCodes) { objectCode = objCode; } return objectCode; } } } return ""; } /** * @return a Set of all financial object codes currently used by accounting lines */ protected Set<String> getAccountingLineObjectCodes(TravelReimbursementForm form) { Set<String> codes = new HashSet<String>(); for (AccountingLine line : (List<AccountingLine>)form.getTravelDocument().getSourceAccountingLines()) { codes.add(line.getFinancialObjectCode()); } return codes; } /** * Should a new TR created from a TR initialize per diem and copy expenses? * Not if there is already a FINAL/PROCESSED TR * * @param newReimbursementDocument * @return */ protected boolean isCopyPerDiemAndExpenses(TravelReimbursementDocument newReimbursementDocument) { List<TravelReimbursementDocument> reimbursementDocuments = getTravelDocumentService().findReimbursementDocuments(newReimbursementDocument.getTravelDocumentIdentifier()); if (!reimbursementDocuments.isEmpty()) { for(TravelReimbursementDocument reimbursementDocument : reimbursementDocuments) { if (reimbursementDocument.getDocumentHeader().getWorkflowDocument().isFinal() || reimbursementDocument.getDocumentHeader().getWorkflowDocument().isProcessed()) { //a finalized or processed TR exists- not okay to set up per diem or initialize expenses return false; } } } //no TRs exist or there aren't any which have been finalized or processed- okay to set up per diem and initialize expenses return true; } /** * Guarantee trip id on form is cleared out before copy - this will guarantee we can initiate the copied document (as there are no restrictions on copies) * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#copy(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward copy(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { TravelReimbursementForm reimbForm = (TravelReimbursementForm)form; reimbForm.setTravelDocumentIdentifier(null); reimbForm.setHistory(new ArrayList<Serializable>()); return super.copy(mapping, form, request, response); } /** * Forward to url to create new reimbursement * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward newReimbursement(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final TravelReimbursementDocument travelReimb = ((TravelReimbursementForm)form).getTravelReimbursementDocument(); return new ActionForward(buildNewReimbursementUrl(travelReimb), true); } protected TravelReimbursementService getTravelReimbursementService() { return SpringContext.getBean(TravelReimbursementService.class); } protected TravelAuthorizationService getTravelAuthorizationService() { return SpringContext.getBean(TravelAuthorizationService.class); } protected ExpenseSummaryReportService getExpenseSummaryReportService() { return SpringContext.getBean(ExpenseSummaryReportService.class); } protected SummaryByDayReportService getSummaryByDayReportService() { return SpringContext.getBean(SummaryByDayReportService.class); } protected NonEmployeeCertificationReportService getNonEmployeeCertificationReportService() { return SpringContext.getBean(NonEmployeeCertificationReportService.class); } protected DateTimeService getDateTimeService() { return SpringContext.getBean(DateTimeService.class); } }