/* * 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; import java.sql.Date; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.kuali.kfs.fp.document.DisbursementVoucherDocument; import org.kuali.kfs.gl.businessobject.Encumbrance; import org.kuali.kfs.gl.service.EncumbranceService; import org.kuali.kfs.module.tem.TemConstants; import org.kuali.kfs.module.tem.TemConstants.ExpenseType; import org.kuali.kfs.module.tem.TemConstants.TravelDocTypes; import org.kuali.kfs.module.tem.TemConstants.TravelParameters; import org.kuali.kfs.module.tem.TemConstants.TravelReimbursementParameters; import org.kuali.kfs.module.tem.TemConstants.TravelReimbursementStatusCodeKeys; import org.kuali.kfs.module.tem.TemParameterConstants; import org.kuali.kfs.module.tem.TemWorkflowConstants; import org.kuali.kfs.module.tem.businessobject.ActualExpense; import org.kuali.kfs.module.tem.businessobject.TravelAdvance; import org.kuali.kfs.module.tem.businessobject.TripType; import org.kuali.kfs.module.tem.document.service.TravelAuthorizationService; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.businessobject.AccountingLine; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.document.AmountTotaling; import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.kew.api.document.DocumentStatus; import org.kuali.rice.kew.api.exception.WorkflowException; import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange; import org.kuali.rice.kim.api.identity.IdentityService; import org.kuali.rice.kim.api.identity.Person; import org.kuali.rice.kim.api.identity.PersonService; import org.kuali.rice.kim.api.identity.principal.Principal; import org.kuali.rice.krad.UserSession; import org.kuali.rice.krad.bo.Note; import org.kuali.rice.krad.dao.DocumentDao; import org.kuali.rice.krad.document.Document; import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.DocumentService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADPropertyConstants; import org.kuali.rice.krad.util.ObjectUtils; /** * Travel Reimbursement Document */ @Entity @Table(name = "TEM_TRVL_REIMB_DOC_T") public class TravelReimbursementDocument extends TEMReimbursementDocument implements AmountTotaling { public static Logger LOG = Logger.getLogger(TravelReimbursementDocument.class); private volatile static IdentityService identityService; @Column(name = "final_reimb_ind", nullable = false, length = 1) private Boolean finalReimbursement = Boolean.FALSE; private Boolean employeeCertification = Boolean.FALSE; private String contactName; private String contactPhoneNum; private String contactEmailAddress; private String contactCampusCode; private KualiDecimal perDiemAdjustment; private KualiDecimal travelAdvanceAmount = KualiDecimal.ZERO; @Transient private KualiDecimal reimbursableAmount = KualiDecimal.ZERO; @Transient private List<TravelAdvance> travelAdvances; public TravelReimbursementDocument() { } public Boolean getFinalReimbursement() { return finalReimbursement; } public void setFinalReimbursement(final Boolean finalReimbursement) { this.finalReimbursement = finalReimbursement; } @Column(name = "emp_cert_ind", nullable = false, length = 1) public Boolean getEmployeeCertification() { return employeeCertification; } public void setEmployeeCertification(final Boolean employeeCertification) { this.employeeCertification = employeeCertification; } public Boolean getNonEmployeeCertification() { return !employeeCertification; } public void setNonEmployeeCertification(final Boolean employeeCertification) { this.employeeCertification = !employeeCertification; } @Column(name = "con_nm", length = 40, nullable = false) public String getContactName() { return contactName; } public void setContactName(String contactName) { this.contactName = contactName; } @Column(name = "con_phone_nbr", length = 20, nullable = false) public String getContactPhoneNum() { return contactPhoneNum; } public void setContactPhoneNum(String contactPhoneNum) { this.contactPhoneNum = contactPhoneNum; } @Column(name = "con_email_addr", length = 40, nullable = true) public String getContactEmailAddress() { return contactEmailAddress; } public void setContactEmailAddress(String contactEmailAddress) { this.contactEmailAddress = contactEmailAddress; } @Column(name = "con_campus_cd", length = 2, nullable = true) public String getContactCampusCode() { return contactCampusCode; } public void setContactCampusCode(String contactCampusCode) { this.contactCampusCode = contactCampusCode; } @Override public void setPerDiemAdjustment(final KualiDecimal perDiemAdjustment) { this.perDiemAdjustment = perDiemAdjustment; } @Override public KualiDecimal getPerDiemAdjustment() { return perDiemAdjustment; } /** * Gets the travelAdvanceAmount attribute. This is the travel advance amount applied on this TR. * @return Returns the travelAdvanceAmount. */ public KualiDecimal getTravelAdvanceAmount() { return travelAdvanceAmount; } /** * Sets the travelAdvanceAmount attribute value. This is the travel advance amount applied on this TR. * @param travelAdvanceAmount The travelAdvanceAmount to set. */ public void setTravelAdvanceAmount(KualiDecimal travelAdvanceAmount) { this.travelAdvanceAmount = travelAdvanceAmount; } public KualiDecimal getReimbursableAmount() { return reimbursableAmount; } public void setReimbursableAmount(KualiDecimal reimbursableAmount) { this.reimbursableAmount = reimbursableAmount; } /** * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#doRouteStatusChange(org.kuali.rice.kew.dto.DocumentRouteStatusChange) */ @Override public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) { super.doRouteStatusChange(statusChangeEvent); LOG.debug("Handling route status change"); if (DocumentStatus.PROCESSED.getCode().equals(statusChangeEvent.getNewRouteStatus())) { // 1. process the reimbursement on the TR try { // update the status to Dept approved updateAndSaveAppDocStatus(TravelReimbursementStatusCodeKeys.DEPT_APPROVED); getTravelReimbursementService().processCustomerReimbursement(this); } catch (Exception e) { LOG.error("Could not spawn CRM or DV on FINAL for travel id " + getTravelDocumentIdentifier()); LOG.error(e.getMessage(), e); } try { final TravelAuthorizationDocument openAuthorization = getTravelReimbursementService().getRelatedOpenTravelAuthorizationDocument(this); List<Document> authorizations = getTravelDocumentService().getDocumentsRelatedTo(this, TravelDocTypes.TRAVEL_AUTHORIZATION_DOCUMENT); // 2. if there is an authorization and there isn't a TAC then we process dis-encumbrance if (openAuthorization != null) { // check TAC existance List<Document> relatedCloseDocuments = getTravelDocumentService().getDocumentsRelatedTo(openAuthorization, TravelDocTypes.TRAVEL_AUTHORIZATION_CLOSE_DOCUMENT); // Trip is encumbrance and no TAC(TAC doc would have handled dis encumbrance) and this is the final TR...so it needs to spawn a TAC if (relatedCloseDocuments.isEmpty() && getFinalReimbursement()) { // save our GLPE's so that the TAC finds them when calculating encumbrances getBusinessObjectService().save(getGeneralLedgerPendingEntries()); // store this so we can reset after we're finished final String initiatorId = getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId(); final Principal initiator = getIdentityService().getPrincipal(initiatorId); GlobalVariables.doInNewGlobalVariables(new UserSession(initiator.getPrincipalName()), new Callable<Object>(){ @Override public Object call() { //close the authorization getTravelAuthorizationService().closeAuthorization(openAuthorization, getTripDescription(), initiator.getPrincipalName(), getDocumentNumber()); return null; } }); } } // if open authorization is null, try to check if there is ANY authorization at all (may be it was closed manually; so its not opened) else if (!authorizations.isEmpty()){ // authorization that is not opened; note to the TR document Note newNote = getDocumentService().createNoteFromDocument(this, "TA is no longer Open; skip Dis-encumberance process."); addNote(newNote); getDocumentDao().save(this); } }catch (Exception e) { LOG.error("Could Add notes for annotation to TR doc #" + getDocumentNumber()); LOG.error(e.getMessage(), e); } } } /** * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#toCopy() */ @Override public void toCopy() throws WorkflowException { super.toCopy(); getNotes().clear(); addContactInformation(); employeeCertification = Boolean.FALSE; } /** * Adds contact information of the initiator of the {@link TravelReimbursementDocument} instance * to the {@link TravelReimbursementDocument}. Initiator is determined as the current person in the * user session * * @param document to modify */ public void addContactInformation() { final String initiatorName = GlobalVariables.getUserSession().getPrincipalName(); final Person initiator = getPersonService().getPersonByPrincipalName(initiatorName); this.setContactName(initiator.getName()); this.setContactPhoneNum(initiator.getPhoneNumber()); this.setContactEmailAddress(initiator.getEmailAddress()); this.setContactCampusCode(initiator.getCampusCode()); } /** * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#initiateDocument() */ @Override public void initiateDocument() { super.initiateDocument(); setApplicationDocumentStatus(TravelReimbursementStatusCodeKeys.IN_PROCESS); getTravelPayment().setDocumentationLocationCode(getParameterService().getParameterValueAsString(TravelReimbursementDocument.class, TravelParameters.DOCUMENTATION_LOCATION_CODE, getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class,TravelParameters.DOCUMENTATION_LOCATION_CODE))); } /** * Provides answers to the following splits: PurchaseWasReceived VendorIsEmployeeOrNonResidentAlien * * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String) */ @Override public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException { if (nodeName.equals(TemWorkflowConstants.DIVISION_APPROVAL_REQUIRED)) { return requiresDivisionApprovalRouting() && isNotAutomaticReimbursement(); } if (nodeName.equals(TemWorkflowConstants.SPECIAL_REQUEST)) { return (requiresSpecialRequestReviewRouting() || (isDelinquent() && !hasDelinquencyException())) && isNotAutomaticReimbursement(); } if (nodeName.equals(TemWorkflowConstants.INTL_TRAVEL)) { return requiresInternationalTravelReviewRouting() && isNotAutomaticReimbursement(); } if (nodeName.equals(TemWorkflowConstants.TAX_MANAGER_APPROVAL_REQUIRED)) { return requiresTaxManagerApprovalRouting() && isNotAutomaticReimbursement(); } if (StringUtils.equals(TemWorkflowConstants.REQUIRES_BUDGET_REVIEW, nodeName)) { return isBudgetReviewRequired(); } if (nodeName.equals(TemWorkflowConstants.SEPARATION_OF_DUTIES)) { return requiresSeparationOfDutiesRouting() && isNotAutomaticReimbursement(); } if (nodeName.equals(TemWorkflowConstants.REQUIRES_TRAVELER_REVIEW)){ return requiresTravelerApprovalRouting(); } if (nodeName.equals(TemWorkflowConstants.REQUIRES_AWARD) || nodeName.equals(TemWorkflowConstants.REQUIRES_SUB_FUND)) { return isNotAutomaticReimbursement(); } throw new UnsupportedOperationException("Cannot answer split question for this node you call \"" + nodeName + "\""); } /** * * @return */ private boolean isNotAutomaticReimbursement() { boolean enabled = getParameterService().getParameterValueAsBoolean(TravelReimbursementDocument.class, TemConstants.TravelReimbursementParameters.AUTOMATIC_APPROVALS_IND); if (!enabled) { return true; } if (!ObjectUtils.isNull(getTraveler()) && !StringUtils.equals(getTraveler().getTravelerTypeCode(), TemConstants.EMP_TRAVELER_TYP_CD)) { return true; } if (getActualExpenses() != null && getActualExpenses().size() > 0) { for (ActualExpense expense : getActualExpenses()) { if (expense.getExpenseTypeObjectCode() != null && expense.getExpenseTypeObjectCode().isReceiptRequired() && getTravelExpenseService().isTravelExpenseExceedReceiptRequirementThreshold(expense) ) { return true; } } } final List<Document> authorizations = getTravelDocumentService().getDocumentsRelatedTo(this, TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_DOCUMENT, TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_AMEND_DOCUMENT); if (authorizations != null && !authorizations.isEmpty()) { for (Document doc : authorizations) { TravelAuthorizationDocument auth = (TravelAuthorizationDocument)doc; if (!ObjectUtils.isNull(auth.getTravelAdvance()) && auth.shouldProcessAdvanceForDocument() && (KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_WIRE.equals(auth.getAdvanceTravelPayment().getPaymentMethodCode()) || KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_DRAFT.equals(auth.getAdvanceTravelPayment().getPaymentMethodCode()))) { return true; } } } KualiDecimal trTotal = KualiDecimal.ZERO; List<AccountingLine> lines = getSourceAccountingLines(); for (AccountingLine line : lines) { trTotal = trTotal.add(line.getAmount()); } Map<String, String> fieldValues = new HashMap<String, String>(); fieldValues.put(KRADPropertyConstants.CODE, this.getTripTypeCode()); TripType tripType = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(TripType.class, fieldValues); KualiDecimal threshold = tripType.getAutoTravelReimbursementLimit(); if (trTotal.isGreaterEqual(threshold)) { return true; } return false; } /** * @return true if the TR is delinquent, false otherwise */ protected boolean isDelinquent() { return !StringUtils.isBlank(getDelinquentAction()); } /** * @return true if this reimbursement has a delinquincy exception, false otherwise */ protected boolean hasDelinquencyException() { if (getDelinquentTRException() != null && getDelinquentTRException().booleanValue()) { return true; } // didn't find it? Then we need to look at our current TA final TravelAuthorizationDocument travelAuth = getTravelDocumentService().findCurrentTravelAuthorization(this); if (travelAuth != null && travelAuth.getDelinquentTRException() != null) { return travelAuth.getDelinquentTRException().booleanValue(); } return false; } /** * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#requiresInternationalTravelReviewRouting() */ @Override protected boolean requiresInternationalTravelReviewRouting() { return super.requiresInternationalTravelReviewRouting() && requiresDivisionApprovalRouting(); } /** * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#requiresDivisionApprovalRouting() */ @Override protected boolean requiresDivisionApprovalRouting() { boolean reqDivApproval = false; KualiDecimal trTotal = getTravelDocumentService().getTotalCumulativeReimbursements(this); KualiDecimal divApprovalMax = new KualiDecimal(getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.CUMULATIVE_REIMBURSABLE_AMOUNT_WITHOUT_DIVISION_APPROVAL)); return (trTotal.isGreaterThan(divApprovalMax)) && requiresAccountingReviewRouting(); } /** * @return the current open amount of the encumbrance(s) created by the related travel authorizations */ public KualiDecimal getTotalAuthorizedEncumbrance() { final KualiDecimal taTotal = getTravelDocumentService().getTotalAuthorizedEncumbrance(this); return taTotal; } /** * @return the total cumulative reimbursements so far */ public KualiDecimal getTotalCumulativeReimbursements() { final KualiDecimal trTotal = getTravelDocumentService().getTotalCumulativeReimbursements(this); return trTotal; } /** * * @return */ private boolean requiresAccountingReviewRouting() { // start with getting the TA encumbrance amount String percent = getParameterService().getParameterValueAsString(TravelReimbursementDocument.class, TravelReimbursementParameters.REIMBURSEMENT_PERCENT_OVER_ENCUMBRANCE_AMOUNT); KualiDecimal taTotal = getTravelDocumentService().getTotalAuthorizedEncumbrance(this); if (taTotal.isLessEqual(KualiDecimal.ZERO)) { return false; } KualiDecimal trTotal = getTravelDocumentService().getTotalCumulativeReimbursements(this); if (trTotal.isLessEqual(KualiDecimal.ZERO)) { return false; } if (trTotal.isGreaterThan(taTotal)) { KualiDecimal subAmount = trTotal.subtract(taTotal); KualiDecimal percentOver = (subAmount.divide(taTotal)).multiply(new KualiDecimal(100)); return (percentOver.isGreaterThan(new KualiDecimal(percent))); } return false; } /** * * @return */ public DocumentDao getDocumentDao() { return SpringContext.getBean(DocumentDao.class); } /** * Overridden to take out advances amount * @see org.kuali.kfs.module.tem.document.TEMReimbursementDocument#getPaymentAmount() */ @Override public KualiDecimal getPaymentAmount() { final KualiDecimal paymentAmountBeforeAdvances = super.getPaymentAmount(); final KualiDecimal paymentAmountAfterAdvances = paymentAmountBeforeAdvances.subtract(getAdvancesTotal()); if (paymentAmountAfterAdvances.isLessThan(KualiDecimal.ZERO)) { return KualiDecimal.ZERO; } return paymentAmountAfterAdvances; } /** * @return all travel advances associated with the trip this document is reimbursing */ public List<TravelAdvance> getTravelAdvances() { if (travelAdvances == null) { travelAdvances = getTravelDocumentService().getTravelAdvancesForTrip(getTravelDocumentIdentifier()); } return travelAdvances; } /** * * @return */ public KualiDecimal getAdvancesTotal() { KualiDecimal retval = KualiDecimal.ZERO; if (getTravelAdvanceAmount().isZero()) { retval = getTravelReimbursementService().getInvoiceAmount(this); // Note that the travelAdvanceAmount is not set here. It is only set when the APP doc is created. } else { retval = getTravelAdvanceAmount(); } return retval; } /** * @see org.kuali.kfs.module.tem.document.TEMReimbursementDocument#getReimbursableGrandTotal() */ @Override public KualiDecimal getReimbursableGrandTotal() { KualiDecimal grandTotal = getReimbursableTotal(); KualiDecimal advancesTotal = getAdvancesTotal(); if (advancesTotal.isGreaterThan(grandTotal)) { return KualiDecimal.ZERO; // if advances are greater than what is being reimbursed, then the grand total is a big fat goose egg. With two equally sized goose eggs after the decimal point. } final KualiDecimal reimbursableGrandTotal = grandTotal.subtract(getAdvancesTotal()); final KualiDecimal reimbursableGrandTotalAfterExpenseLimit = applyExpenseLimit(reimbursableGrandTotal); return reimbursableGrandTotalAfterExpenseLimit; } /** * @see org.kuali.kfs.module.tem.document.TravelDocument#getReportPurpose() */ @Override public String getReportPurpose() { return getTripDescription(); } /** * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#populateVendorPayment(org.kuali.kfs.fp.document.DisbursementVoucherDocument) */ @Override public void populateVendorPayment(DisbursementVoucherDocument disbursementVoucherDocument) { super.populateVendorPayment(disbursementVoucherDocument); String locationCode = getParameterService().getParameterValueAsString(TravelReimbursementDocument.class, TravelParameters.DOCUMENTATION_LOCATION_CODE, getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class,TravelParameters.DOCUMENTATION_LOCATION_CODE)); String startDate = new SimpleDateFormat("MM/dd/yyyy").format(this.getTripBegin()); String endDate = new SimpleDateFormat("MM/dd/yyyy").format(this.getTripEnd()); String checkStubText = this.getTravelDocumentIdentifier() + ", " + this.getPrimaryDestinationName() + ", " + startDate + " - " + endDate; disbursementVoucherDocument.setDisbursementVoucherDocumentationLocationCode(locationCode); disbursementVoucherDocument.setDisbVchrCheckStubText(checkStubText); } /** * Retrieves all encumbrances associated with this trip and adds up their amounts * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#getEncumbranceTotal() */ @Transient @Override public KualiDecimal getEncumbranceTotal() { KualiDecimal encTotal = KualiDecimal.ZERO; final List<Encumbrance> encumbrances = getTravelEncumbranceService().getEncumbrancesForTrip(getTravelDocumentIdentifier(), getDocumentNumber()); for (Encumbrance enc : encumbrances) { encTotal = encTotal.add(enc.getAccountLineEncumbranceAmount()); } return encTotal; } /** * Returns TRCA * @see org.kuali.kfs.module.tem.document.TEMReimbursementDocument#getAchCheckDocumentType() */ @Override public String getAchCheckDocumentType() { return TemConstants.TravelDocTypes.TRAVEL_REIMBURSEMENT_CHECK_ACH_DOCUMENT; } /** * Returns TRWF * @see org.kuali.kfs.module.tem.document.TEMReimbursementDocument#getWireTransferOrForeignDraftDocumentType() */ @Override public String getWireTransferOrForeignDraftDocumentType() { return TemConstants.TravelDocTypes.TRAVEL_REIMBURSEMENT_WIRE_OR_FOREIGN_DRAFT_DOCUMENT; } /** * Generate TR disencumbrance glpe's * @see org.kuali.kfs.module.tem.document.TEMReimbursementDocument#generateDocumentGeneralLedgerPendingEntries(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper) */ @Override public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) { boolean result = super.generateDocumentGeneralLedgerPendingEntries(sequenceHelper); if (isTripGenerateEncumbrance()) { getTravelEncumbranceService().disencumberTravelReimbursementFunds(this, sequenceHelper); } getTravelReimbursementService().generateEntriesForAdvances(this, sequenceHelper); return result; } /** * Overridden to handle the explicit entries generated for travel advance clearing and crediting accounting lines * @see org.kuali.kfs.module.tem.document.TEMReimbursementDocument#customizeExplicitGeneralLedgerPendingEntry(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry) */ @Override public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) { super.customizeExplicitGeneralLedgerPendingEntry(postable, explicitEntry); if (postable instanceof AccountingLine) { final AccountingLine accountingLine = (AccountingLine)postable; if (TemConstants.TRAVEL_ADVANCE_CLEARING_LINE_TYPE_CODE.equals(accountingLine.getFinancialDocumentLineTypeCode())) { explicitEntry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE); explicitEntry.setFinancialDocumentTypeCode(TemConstants.TravelDocTypes.TRAVEL_REIMBURSEMENT_TRAVEL_ADVANCES_DOCUMENT); } else if (TemConstants.TRAVEL_ADVANCE_CREDITING_LINE_TYPE_CODE.equals(accountingLine.getFinancialDocumentLineTypeCode())) { explicitEntry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE); explicitEntry.setFinancialDocumentTypeCode(TemConstants.TravelDocTypes.TRAVEL_REIMBURSEMENT_TRAVEL_ADVANCES_DOCUMENT); } } } /** * This method returns total expense amount minus the non-reimbursable. Overridden to remove manually adjusted per diem if it exists * * @return */ @Override public KualiDecimal getApprovedAmount() { KualiDecimal total = KualiDecimal.ZERO; for (ExpenseType expense : EnumSet.allOf(ExpenseType.class)){ KualiDecimal expenseAmount = getTravelExpenseService().getExpenseServiceByType(expense).getAllExpenseTotal(this, false); if (expenseAmount == null) { expenseAmount = KualiDecimal.ZERO; } total = expenseAmount.add(total); if (expense.equals(ExpenseType.perDiem) && getPerDiemAdjustment() != null && getPerDiemAdjustment().isPositive() && expenseAmount.isPositive()) { total = total.subtract(getPerDiemAdjustment()); } } return total; } protected PersonService getPersonService() { return SpringContext.getBean(PersonService.class); } protected DocumentService getDocumentService() { return SpringContext.getBean(DocumentService.class); } protected TravelAuthorizationService getTravelAuthorizationService() { return SpringContext.getBean(TravelAuthorizationService.class); } protected EncumbranceService getEncumbranceService() { return SpringContext.getBean(EncumbranceService.class); } protected GeneralLedgerPendingEntryService getGeneralLedgerPendingEntryService() { return SpringContext.getBean(GeneralLedgerPendingEntryService.class); } /** * @return the default implementation of IdentityService */ protected IdentityService getIdentityService() { if (identityService == null) { identityService = SpringContext.getBean(IdentityService.class); } return identityService; } /** * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#getDisapprovedAppDocStatusMap() */ @Override public Map<String, String> getDisapprovedAppDocStatusMap() { return TravelReimbursementStatusCodeKeys.getDisapprovedAppDocStatusMap(); } @Override protected String generateDescription() { DateTimeService dateTimeService = SpringContext.getBean(DateTimeService.class); String description = super.generateDescription(); if (getTripEnd() != null && getTripBegin() != null) { boolean preTripReimbursement = false; try { Date tripEnd = dateTimeService.convertToSqlDate(getTripEnd()); Date tripBegin = dateTimeService.convertToSqlDate(getTripBegin()); Date currentDate = dateTimeService.getCurrentSqlDate(); preTripReimbursement = tripBegin.compareTo(currentDate)>=0 && tripEnd.compareTo(currentDate) >= 0 ? true : false; } catch (ParseException pe) { LOG.error("Error while parsing dates ",pe); } final boolean preTrip = getParameterService().getParameterValueAsBoolean(TravelReimbursementDocument.class, TemConstants.TravelReimbursementParameters.PRETRIP_REIMBURSEMENT_IND, false); if (preTrip && preTripReimbursement){ return postpendPreTripToDescription(description); } } return description; } /** * Adds (Pre-Trip) to the end of the given String (presumably the document description), and then makes sure it will fit within the doc description's max length * @param description the description to add (Pre-Trip) to * @return the fitted String */ protected String postpendPreTripToDescription(String description) { final String postPendedDescription = TemConstants.TRAVEL_REIMBURSEMENT_PRETRIP_DESCRIPTION_TEXT + description ; final int maxLength = getDataDictionaryService().getAttributeMaxLength(getDocumentHeader().getClass(), KFSPropertyConstants.DOCUMENT_DESCRIPTION); final String fittedDescription = (postPendedDescription.length() > maxLength) ? postPendedDescription.substring(0, maxLength) : postPendedDescription; return fittedDescription; } /** * Checks the check stub text for the payment * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#prepareForSave(org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent) */ @Override public void prepareForSave(KualiDocumentEvent event) { super.prepareForSave(event); getTravelPayment().setCheckStubText(getTravelDocumentIdentifier() + " " + StringUtils.defaultString(getTripDescription()) + " " + getTripBegin()); } }