/*
* 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 static org.kuali.kfs.module.tem.TemKeyConstants.AGENCY_SITES_URL;
import static org.kuali.kfs.module.tem.TemKeyConstants.ENABLE_AGENCY_SITES_URL;
import static org.kuali.kfs.module.tem.TemKeyConstants.PASS_TRIP_ID_TO_AGENCY_SITES;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
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.service.SufficientFundsService;
import org.kuali.kfs.integration.ar.AccountsReceivableCustomer;
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.TemKeyConstants;
import org.kuali.kfs.module.tem.TemParameterConstants;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.businessobject.ActualExpense;
import org.kuali.kfs.module.tem.businessobject.ClassOfService;
import org.kuali.kfs.module.tem.businessobject.ExpenseTypeObjectCode;
import org.kuali.kfs.module.tem.businessobject.GroupTraveler;
import org.kuali.kfs.module.tem.businessobject.HistoricalTravelExpense;
import org.kuali.kfs.module.tem.businessobject.ImportedExpense;
import org.kuali.kfs.module.tem.businessobject.PerDiemExpense;
import org.kuali.kfs.module.tem.businessobject.PrimaryDestination;
import org.kuali.kfs.module.tem.businessobject.SpecialCircumstances;
import org.kuali.kfs.module.tem.businessobject.TemExpense;
import org.kuali.kfs.module.tem.businessobject.TemProfile;
import org.kuali.kfs.module.tem.businessobject.TemSourceAccountingLine;
import org.kuali.kfs.module.tem.businessobject.TransportationModeDetail;
import org.kuali.kfs.module.tem.businessobject.TravelerDetail;
import org.kuali.kfs.module.tem.businessobject.TravelerType;
import org.kuali.kfs.module.tem.businessobject.TripType;
import org.kuali.kfs.module.tem.document.service.TravelDocumentService;
import org.kuali.kfs.module.tem.document.service.TravelReimbursementService;
import org.kuali.kfs.module.tem.service.AccountingDistributionService;
import org.kuali.kfs.module.tem.service.PerDiemService;
import org.kuali.kfs.module.tem.service.TemProfileService;
import org.kuali.kfs.module.tem.service.TemRoleService;
import org.kuali.kfs.module.tem.service.TravelDocumentNotificationService;
import org.kuali.kfs.module.tem.service.TravelEncumbranceService;
import org.kuali.kfs.module.tem.service.TravelExpenseService;
import org.kuali.kfs.module.tem.service.TravelService;
import org.kuali.kfs.module.tem.service.TravelerService;
import org.kuali.kfs.module.tem.util.GroupTravelerComparator;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.businessobject.Bank;
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.businessobject.SourceAccountingLine;
import org.kuali.kfs.sys.businessobject.SufficientFundsItem;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.AccountingDocumentBase;
import org.kuali.kfs.sys.service.BankService;
import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.kfs.sys.util.KfsDateUtils;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
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.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.kim.api.role.RoleService;
import org.kuali.rice.krad.bo.Note;
import org.kuali.rice.krad.comparator.StringValueComparator;
import org.kuali.rice.krad.document.Copyable;
import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.SequenceAccessorService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADPropertyConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
/**
* Abstract Travel Document Base
*/
public abstract class TravelDocumentBase extends AccountingDocumentBase implements TravelDocument, Copyable {
protected static Logger LOG = Logger.getLogger(TravelDocumentBase.class);
private TripType tripType;
private String tripTypeCode;
private Timestamp tripBegin;
private Timestamp tripEnd;
private String tripDescription;
private Boolean primaryDestinationIndicator = false;
private Integer primaryDestinationId;
private PrimaryDestination primaryDestination;
private String primaryDestinationName;
private String primaryDestinationCountryState;
private String primaryDestinationCounty;
private KualiDecimal rate;
private KualiDecimal expenseLimit;
private String mealWithoutLodgingReason;
private String dummyAppDocStatus;
private String financialDocumentBankCode;
protected boolean tripProgenitor;
// Traveler section
private Integer temProfileId;
private TemProfile temProfile;
private Integer travelerDetailId;
private TravelerDetail traveler;
private Bank bank;
protected List<SpecialCircumstances> specialCircumstances = new ArrayList<SpecialCircumstances>();
protected List<GroupTraveler> groupTravelers = new ArrayList<GroupTraveler>();
protected List<PerDiemExpense> perDiemExpenses = new ArrayList<PerDiemExpense>();
protected List<ActualExpense> actualExpenses = new ArrayList<ActualExpense>();
protected List<ImportedExpense> importedExpenses = new ArrayList<ImportedExpense>();
protected List<HistoricalTravelExpense> historicalTravelExpenses = new ArrayList<HistoricalTravelExpense>();
protected String travelDocumentIdentifier;
protected Integer travelDocumentLinkIdentifier;
private Boolean delinquentTRException = false;
private Boolean blanketTravel = false;
protected volatile transient static TravelEncumbranceService travelEncumbranceService;
@Transient
private List<PropertyChangeListener> propertyChangeListeners = new ArrayList<PropertyChangeListener>();
@Transient
private Map<String, String> disabledProperties;
@Transient
private Boolean taxSelectable = Boolean.TRUE;
protected TravelDocumentBase() {
super();
}
protected TravelDocumentService getTravelDocumentService() {
return SpringContext.getBean(TravelDocumentService.class);
}
protected TravelEncumbranceService getTravelEncumbranceService() {
if (travelEncumbranceService == null) {
travelEncumbranceService = SpringContext.getBean(TravelEncumbranceService.class);
}
return travelEncumbranceService;
}
protected TravelReimbursementService getTravelReimbursementService() {
return SpringContext.getBean(TravelReimbursementService.class);
}
protected TemRoleService getTemRoleService() {
return SpringContext.getBean(TemRoleService.class);
}
protected TemProfileService getTemProfileService() {
return SpringContext.getBean(TemProfileService.class);
}
@Override
protected ParameterService getParameterService() {
return SpringContext.getBean(ParameterService.class);
}
protected ConfigurationService getConfigurationService() {
return SpringContext.getBean(ConfigurationService.class);
}
protected TravelService getTravelService() {
return SpringContext.getBean(TravelService.class);
}
protected TravelExpenseService getTravelExpenseService() {
return SpringContext.getBean(TravelExpenseService.class);
}
protected TravelerService getTravelerService() {
return SpringContext.getBean(TravelerService.class);
}
@Override
protected BusinessObjectService getBusinessObjectService() {
return SpringContext.getBean(BusinessObjectService.class);
}
protected SequenceAccessorService getSequenceAccessorService() {
return SpringContext.getBean(SequenceAccessorService.class);
}
protected WorkflowDocumentService getWorkflowDocumentService() {
return SpringContext.getBean(WorkflowDocumentService.class);
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getAccountingDistributionService()
*/
@Override
public AccountingDistributionService getAccountingDistributionService(){
return SpringContext.getBean(AccountingDistributionService.class);
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getAppDocStatus()
*/
@Override
public String getAppDocStatus() {
String status = getDocumentHeader().getWorkflowDocument().getApplicationDocumentStatus();
return status;
}
/**
* Save the current document header with the updated app doc status
*
* @return
*/
final public boolean saveAppDocStatus() {
boolean saved = false;
try {
getWorkflowDocumentService().save(getDocumentHeader().getWorkflowDocument(), null);
saved = true;
}
catch (WorkflowException ex) {
LOG.error(ex.getMessage(), ex);
}
return saved;
}
/**
* Gets the dummyAppDocStatus attribute.
* @return Returns the dummyAppDocStatus.
*/
public String getDummyAppDocStatus() {
return dummyAppDocStatus;
}
/**
* Sets the dummyAppDocStatus attribute value.
* @param dummyAppDocStatus The dummyAppDocStatus to set.
*/
public void setDummyAppDocStatus(String dummyAppDocStatus) {
this.dummyAppDocStatus = dummyAppDocStatus;
}
/**
* Gets the primaryDestinationIndicator attribute.
* @return Returns the primaryDestinationIndicator.
*/
@Override
public Boolean getPrimaryDestinationIndicator() {
return primaryDestinationIndicator;
}
/**
* Sets the primaryDestinationIndicator attribute value.
* @param primaryDestinationIndicator The primaryDestinationIndicator to set.
*/
@Override
public void setPrimaryDestinationIndicator(Boolean primaryDestinationIndicator) {
this.primaryDestinationIndicator = primaryDestinationIndicator;
}
@Column(name = "TRIP_DESC")
@Override
public String getTripDescription() {
return tripDescription;
}
@Override
public Integer getPrimaryDestinationId() {
return primaryDestinationId;
}
@Override
public void setPrimaryDestinationId(Integer primaryDestinationId) {
this.primaryDestinationId = primaryDestinationId;
}
@Override
public PrimaryDestination getPrimaryDestination() {
if (primaryDestination == null){
primaryDestination = new PrimaryDestination();
}
return primaryDestination;
}
@Override
public void setPrimaryDestination(PrimaryDestination primaryDestination) {
this.primaryDestination = primaryDestination;
if (primaryDestination != null){
this.setPrimaryDestinationCountryState(primaryDestination.getRegion().getRegionName());
this.setPrimaryDestinationCounty(primaryDestination.getCounty());
this.setPrimaryDestinationName(primaryDestination.getPrimaryDestinationName());
}
else{
this.setPrimaryDestinationCountryState(null);
this.setPrimaryDestinationCounty(null);
this.setPrimaryDestinationName(null);
}
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getPrimaryDestinationName()
*/
@Override
public String getPrimaryDestinationName() {
return primaryDestinationName;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setPrimaryDestinationName(java.lang.String)
*/
@Override
public void setPrimaryDestinationName(String primaryDestinationName) {
this.primaryDestinationName = primaryDestinationName;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getPrimaryDestinationCountryState()
*/
@Override
public String getPrimaryDestinationCountryState() {
return primaryDestinationCountryState;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setPrimaryDestinationCountryState(java.lang.String)
*/
@Override
public void setPrimaryDestinationCountryState(String primaryDestinationCountryState) {
if (primaryDestinationCountryState != null) {
primaryDestinationCountryState = primaryDestinationCountryState.toUpperCase();
}
this.primaryDestinationCountryState = primaryDestinationCountryState;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getPrimaryDestinationCounty()
*/
@Override
public String getPrimaryDestinationCounty() {
return primaryDestinationCounty;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setPrimaryDestinationCounty(java.lang.String)
*/
@Override
public void setPrimaryDestinationCounty(String primaryDestinationCounty) {
this.primaryDestinationCounty = primaryDestinationCounty;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTripDescription(java.lang.String)
*/
@Override
public void setTripDescription(String tripDescription) {
this.tripDescription = tripDescription;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTripType()
*/
@Override
@ManyToOne
@JoinColumn(name = "TRIP_TYP_CD")
public TripType getTripType() {
return tripType;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTripType(org.kuali.kfs.module.tem.businessobject.TripType)
*/
@Override
public void setTripType(TripType tripType) {
this.tripType = tripType;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTripTypeCode()
*/
@Override
@Column(name = "TRIP_TYP_CD", length = 3)
public String getTripTypeCode() {
return tripTypeCode;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTripTypeCode(java.lang.String)
*/
@Override
public void setTripTypeCode(String tripTypeCode) {
this.tripTypeCode = tripTypeCode;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTravelDocumentIdentifier()
*/
@Override
@Column(name = "TRVL_ID")
public String getTravelDocumentIdentifier() {
return travelDocumentIdentifier;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTravelDocumentIdentifier(java.lang.String)
*/
@Override
public void setTravelDocumentIdentifier(String travelDocumentIdentifier) {
this.travelDocumentIdentifier = travelDocumentIdentifier;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTripBegin()
*/
@Override
@Column(name = "TRIP_BGN_DT")
public Timestamp getTripBegin() {
return tripBegin;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTripBegin(java.sql.Timestamp)
*/
@Override
public void setTripBegin(Timestamp tripBegin) {
this.tripBegin = tripBegin;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTripEnd()
*/
@Override
@Column(name = "TRIP_END_DT")
public Timestamp getTripEnd() {
return tripEnd;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTripEnd(java.sql.Timestamp)
*/
@Override
public void setTripEnd(Timestamp tripEnd) {
this.tripEnd = tripEnd;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#canReturn()
*/
@Override
public boolean canReturn() {
return getDocumentHeader().getWorkflowDocument().isEnroute();
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTravelDocumentLinkIdentifier()
*/
@Override
public Integer getTravelDocumentLinkIdentifier() {
return travelDocumentLinkIdentifier;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTravelDocumentLinkIdentifier(java.lang.Integer)
*/
@Override
public void setTravelDocumentLinkIdentifier(Integer travelDocumentLinkIdentifier) {
this.travelDocumentLinkIdentifier = travelDocumentLinkIdentifier;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTraveler()
*/
@Override
@ManyToOne
@JoinColumn(name = "traveler_dtl_id")
public TravelerDetail getTraveler() {
return traveler;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTraveler(org.kuali.kfs.module.tem.businessobject.TravelerDetail)
*/
@Override
public void setTraveler(TravelerDetail traveler) {
this.traveler = traveler;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTravelerDetailId()
*/
@Override
@Column(name = "traveler_dtl_id")
public Integer getTravelerDetailId() {
return travelerDetailId;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTravelerDetailId(java.lang.Integer)
*/
@Override
public void setTravelerDetailId(Integer travelerDetailId) {
this.travelerDetailId = travelerDetailId;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getExpenseLimit()
*/
@Override
@Column(name = "expenseLimit", precision = 19, scale = 2)
public KualiDecimal getExpenseLimit() {
return expenseLimit;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setExpenseLimit(org.kuali.rice.kns.util.KualiDecimal)
*/
@Override
public void setExpenseLimit(KualiDecimal expenseLimit) {
this.expenseLimit = expenseLimit;
}
/**
* Sets the propertyChangeListener attribute value.
*
* @param propertyChangeListener The propertyChangeListener to set.
*/
public void setPropertyChangeListeners(final List<PropertyChangeListener> propertyChangeListeners) {
this.propertyChangeListeners = propertyChangeListeners;
}
/**
* Gets the propertyChangeListeners attribute.
*
* @return Returns the propertyChangeListenerDetailId.
*/
public List<PropertyChangeListener> getPropertyChangeListeners() {
return this.propertyChangeListeners;
}
/**
* Notify listeners that an event occurred
*
* @param event the {@link PropertyChangeEvent}
*/
protected void notifyChangeListeners(final PropertyChangeEvent event) {
for (final PropertyChangeListener listener : getPropertyChangeListeners()) {
listener.propertyChange(event);
}
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setSpecialCircumstances(java.util.List)
*/
@Override
public void setSpecialCircumstances(final List<SpecialCircumstances> specialCircumstances) {
this.specialCircumstances = specialCircumstances;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getSpecialCircumstances()
*/
@Override
public List<SpecialCircumstances> getSpecialCircumstances() {
return this.specialCircumstances;
}
/**
* Here we need to generate the travel request number from the SequenceAccessorService as well as set any status for the
* document
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#prepareForSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent)
*/
@Override
public void prepareForSave(KualiDocumentEvent event) {
// Set the identifier and org doc number before anything is done in the super class
if (ObjectUtils.isNull(getTravelDocumentIdentifier())) {
// need retrieve the next available TR id to save in GL entries (only do if travel request id is null which should be on
// first save)
setTravelDocumentIdentifier(generateTripId());
this.getDocumentHeader().setOrganizationDocumentNumber(getTravelDocumentIdentifier());
}
//always generate the description in case the traveler changes from the last time it was generated.
getDocumentHeader().setDocumentDescription(generateDescription());
super.prepareForSave(event);
}
/**
* @return the next sequence id in the TRVL_ID_SEQ
*/
protected String getSequenceForTripId() {
SequenceAccessorService sas = getSequenceAccessorService();
final Long trSequenceNumber = sas.getNextAvailableSequenceNumber("TRVL_ID_SEQ", this.getClass());
return trSequenceNumber.toString();
}
/**
* @return the trip id prefix associated with this document type
*/
protected String getTripIdPrefix() {
return TemConstants.TripIdPrefix.TRIP_PREFIX;
}
/**
* Generates a trip id for a trip. It is based on document prefix + a unique sequence. If the KFS-TEM / Document / INCLUDE_TRAVELER_TYPE_IN_TRIP_ID_IND
* is set to on, then a traveler type ("NON" for non-employee or "EMP" for employee) will be inserted into the prefix between the trip id and the sequence number.
* If a completely different scheme is needed to generate trip id's appropriate to your institution, this is the single method to override
* @return the generated trip id for the trip
*/
public String generateTripId() {
final String travelerType = (getParameterService().getParameterValueAsBoolean(TemParameterConstants.TEM_DOCUMENT.class, TemConstants.TravelParameters.INCLUDE_TRAVELER_TYPE_IN_TRIP_ID_IND, Boolean.FALSE) && !ObjectUtils.isNull(getTraveler()) && !StringUtils.isBlank(getTraveler().getTravelerTypeCode())) ?
getTraveler().getTravelerTypeCode()+"-" :
"";
return getTripIdPrefix() + travelerType + getSequenceForTripId();
}
/**
* This method generates a description based on the following information: principal trip begin date (mm/dd/yyy) primary
* destination and truncate at 40 characters
*
* @return a newly generated description
*/
protected String generateDescription() {
StringBuffer sb = new StringBuffer();
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
PersonService ps = SpringContext.getBean(PersonService.class);
Person person = ps.getPerson(getTraveler().getPrincipalId());
this.getTraveler().refreshReferenceObject(TemPropertyConstants.CUSTOMER);
AccountsReceivableCustomer customer = getTraveler().getCustomer();
if (person != null) {
sb.append(person.getLastName() + ", " + person.getFirstName() + " " + person.getMiddleName() + " ");
}
else if (customer != null) {
sb.append(customer.getCustomerName() + " ");
}
else {
sb.append(getTraveler().getFirstName() + " " + getTraveler().getLastName() + " ");
}
if(this.getTripBegin() != null) {
sb.append(format.format(this.getTripBegin()) + " ");
}
if(!ObjectUtils.isNull(getPrimaryDestination()) && !StringUtils.isBlank(getPrimaryDestination().getPrimaryDestinationName()) && getPrimaryDestinationId() != null && getPrimaryDestinationId().intValue() != TemConstants.CUSTOM_PRIMARY_DESTINATION_ID) {
sb.append(getPrimaryDestination().getPrimaryDestinationName());
} else {
if (!StringUtils.isBlank(getPrimaryDestinationName())) {
sb.append(getPrimaryDestinationName());
}
}
String tempStr = sb.toString();
if (getDelinquentAction() != null) {
tempStr = "(Delinquent) " + tempStr;
}
if (tempStr.length() > 40) {
tempStr = tempStr.substring(0, 39);
}
return tempStr;
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#toCopy()
*/
@SuppressWarnings("rawtypes")
@Override
public void toCopy() throws WorkflowException {
super.toCopy();
cleanTravelDocument();
// Copy Trip Detail Estimates, Traveler, Emergency Contacts, Transportation Modes, and Group Travelers
setTransportationModes(getTravelDocumentService().copyTransportationModeDetails(getTransportationModes(), getDocumentNumber()));
setPerDiemExpenses(getTravelDocumentService().copyPerDiemExpenses(getPerDiemExpenses(), getDocumentNumber()));
setSpecialCircumstances(getTravelDocumentService().copySpecialCircumstances(this.getSpecialCircumstances(), getDocumentNumber()));
setTraveler(getTravelerService().copyTravelerDetail(getTraveler(), getDocumentNumber()));
setGroupTravelers(getTravelDocumentService().copyGroupTravelers(getGroupTravelers(), getDocumentNumber()));
setActualExpenses((List<ActualExpense>) getTravelDocumentService().copyActualExpenses(getActualExpenses(), getDocumentNumber()));
setImportedExpenses(new ArrayList<ImportedExpense>());
// Cleanup notes
setNotes(new ArrayList<Note>());
//set it to in-progress so it will be updated
setApplicationDocumentStatus(TemConstants.TravelStatusCodeKeys.IN_PROCESS);
}
/**
* Cleans the Travel Document Identifier, and the notes
*/
protected void cleanTravelDocument() {
this.travelDocumentIdentifier = null;
this.travelerDetailId = null;
this.getTraveler().setId(null);
this.getDocumentHeader().setOrganizationDocumentNumber("");
this.getDocumentHeader().setDocumentDescription(TemConstants.PRE_FILLED_DESCRIPTION);
this.setPerDiemAdjustment(KualiDecimal.ZERO);
}
/**
*
* @return
*/
@Transient
public boolean isValidExpenses(){
if(this.actualExpenses == null){
return true;
}
int counter = 0;
for(ActualExpense actualExpense: this.actualExpenses){
if (actualExpense.getExpenseDetails().size() > 0){
KualiDecimal detailAmount = actualExpense.getTotalDetailExpenseAmount();
GlobalVariables.getMessageMap().addToErrorPath(KRADPropertyConstants.DOCUMENT);
GlobalVariables.getMessageMap().addToErrorPath(TemPropertyConstants.ACTUAL_EXPENSES + "[" + counter + "]");
if(detailAmount.isGreaterThan(actualExpense.getExpenseAmount()) && !TemConstants.ExpenseTypes.MILEAGE.equals(actualExpense.getExpenseTypeCode())){
GlobalVariables.getMessageMap().putError(TemPropertyConstants.EXPENSE_AMOUNT, TemKeyConstants.ERROR_ACTUAL_EXPENSE_DETAIL_AMOUNT_EXCEED, detailAmount.toString(), actualExpense.getExpenseAmount().toString());
return false;
}
GlobalVariables.getMessageMap().clearErrorPath();
}
counter++;
}
return true;
}
/**
*
* @return
*/
@Transient
public KualiDecimal getMealsAndIncidentalsGrandTotal() {
PerDiemService service = SpringContext.getBean(PerDiemService.class);
return service.getMealsAndIncidentalsGrandTotal(this);
}
/**
*
* @return
*/
@Transient
public KualiDecimal getLodgingGrandTotal() {
PerDiemService service = SpringContext.getBean(PerDiemService.class);
return service.getLodgingGrandTotal(this);
}
/**
*
* @return
*/
@Transient
public KualiDecimal getMileageTotalGrandTotal() {
PerDiemService service = SpringContext.getBean(PerDiemService.class);
return service.getMileageTotalGrandTotal(this);
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getDailyTotalGrandTotal()
*/
@Override
@Transient
public KualiDecimal getDailyTotalGrandTotal() {
PerDiemService service = SpringContext.getBean(PerDiemService.class);
return service.getDailyTotalGrandTotal(this);
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getDocumentGrandTotal()
*/
@Override
@Transient
public KualiDecimal getDocumentGrandTotal() {
KualiDecimal total = KualiDecimal.ZERO;
for (ExpenseType expense : EnumSet.allOf(ExpenseType.class)){
total = getTravelExpenseService().getExpenseServiceByType(expense).getAllExpenseTotal(this, true).add(total);
}
return total;
}
/**
*
* @return
*/
@Transient
public Integer getMilesGrandTotal() {
PerDiemService service = SpringContext.getBean(PerDiemService.class);
return service.getMilesGrandTotal(this);
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getPerDiemExpenses()
*/
@Override
@OneToMany(mappedBy="documentNumber")
public List<PerDiemExpense> getPerDiemExpenses() {
return perDiemExpenses;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setPerDiemExpenses(java.util.List)
*/
@Override
public void setPerDiemExpenses(List<PerDiemExpense> perDiemExpenses) {
this.perDiemExpenses = perDiemExpenses;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getActualExpenses()
*/
@Override
@OneToMany(mappedBy="documentNumber")
public List<ActualExpense> getActualExpenses() {
return actualExpenses;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setActualExpenses(java.util.List)
*/
@Override
public void setActualExpenses(List<ActualExpense> actualExpenses) {
this.actualExpenses = actualExpenses;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#enableExpenseTypeSpecificFields(java.util.List)
*/
@Override
@Transient
public void enableExpenseTypeSpecificFields(final List<ActualExpense> actualExpenses){
for(ActualExpense actualExpense: actualExpenses){
actualExpense.enableExpenseTypeSpecificFields();
}
}
/**
* Returns the pending expense amount after summing up detail rows expense amount
*
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTotalPendingAmount(java.util.List, java.lang.Long)
*/
@Override
@Transient
public KualiDecimal getTotalPendingAmount(ActualExpense actualExpense){
KualiDecimal expenseAmount = actualExpense.getExpenseAmount();
return expenseAmount.subtract(actualExpense.getTotalDetailExpenseAmount());
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getParentExpenseAmount(java.util.List, java.lang.Long)
*/
@Override
@Transient
public KualiDecimal getParentExpenseAmount(List<ActualExpense> actualExpenses, Long id){
for(ActualExpense actualExpense: actualExpenses){
if(actualExpense.getId().equals(id)){
return actualExpense.getExpenseAmount();
}
}
return KualiDecimal.ZERO;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getParentExpenseRecord(java.util.List, java.lang.Long)
*/
@Override
@Transient
public ActualExpense getParentExpenseRecord(List<ActualExpense> actualExpenses, Long id){
for(ActualExpense actualExpense: actualExpenses){
if(actualExpense.getId().equals(id)){
return actualExpense;
}
}
return null;
}
/**
* Totals up all the other expenses. Needs to multiply the expenseAmount by the currencyRate because the currency
* could be from another country other than US. If there is no currencyRate, we assume it's US even if the country isn't.
*
* @return {@link KualiDecimal} with the total
*/
@Override
@Transient
public KualiDecimal getActualExpensesTotal() {
KualiDecimal retval = KualiDecimal.ZERO;
LOG.debug("Getting other expense total");
if(actualExpenses != null){
for (final ActualExpense expense : actualExpenses) {
final KualiDecimal expenseAmount = new KualiDecimal(expense.getExpenseAmount().bigDecimalValue().multiply(expense.getCurrencyRate()));
LOG.debug("Expense amount gotten is " + expenseAmount);
retval = retval.add(expenseAmount);
}
}
LOG.debug("Returning otherExpense Total " + retval);
return retval;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#addActualExpense(org.kuali.kfs.module.tem.businessobject.ActualExpense)
*/
@Override
@Transient
public void addActualExpense(final ActualExpense line) {
final String sequenceName = line.getSequenceName();
final Long sequenceNumber = getSequenceAccessorService().getNextAvailableSequenceNumber(sequenceName, ActualExpense.class);
line.setId(sequenceNumber);
line.setDocumentNumber(this.documentNumber);
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.ACTUAL_EXPENSES, null, line));
line.enableExpenseTypeSpecificFields();
getActualExpenses().add(line);
}
@Override
@Transient
public void addExpense(TemExpense line) {
final String sequenceName = line.getSequenceName();
// Because all expense types use the same sequence, it doesn't matter which class grabs the sequence
final Long sequenceNumber = getSequenceAccessorService().getNextAvailableSequenceNumber(sequenceName, ImportedExpense.class);
line.setId(sequenceNumber);
line.setDocumentNumber(this.documentNumber);
if (line instanceof ActualExpense){
getTravelExpenseService().updateTaxabilityOfActualExpense((ActualExpense)line, this, GlobalVariables.getUserSession().getPerson()); // when adding the expense, attempt to update the taxability if user can't edit taxability
getActualExpenses().add((ActualExpense) line);
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.ACTUAL_EXPENSES, null, line));
((ActualExpense)line).enableExpenseTypeSpecificFields();
}
else{
getImportedExpenses().add((ImportedExpense) line);
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.IMPORTED_EXPENSES, null, line));
}
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#addExpenseDetail(org.kuali.kfs.module.tem.businessobject.TemExpense, java.lang.Integer)
*/
@Override
@Transient
public void addExpenseDetail(TemExpense line, Integer index) {
final String sequenceName = line.getSequenceName();
final Long sequenceNumber = getSequenceAccessorService().getNextAvailableSequenceNumber(sequenceName, ImportedExpense.class);
line.setId(sequenceNumber);
line.setDocumentNumber(this.documentNumber);
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.IMPORTED_EXPENSES, null, line));
if (line instanceof ActualExpense){
getTravelExpenseService().updateTaxabilityOfActualExpense((ActualExpense) line, this, GlobalVariables.getUserSession().getPerson()); // when adding the expense detail, attempt to update the taxability if user can't edit taxability
getActualExpenses().get(index).addExpenseDetails(line);
if (!ObjectUtils.isNull(getTraveler()) && !StringUtils.isBlank(getTraveler().getTravelerTypeCode()) && !StringUtils.isBlank(getTripTypeCode())) {
getActualExpenses().get(index).refreshExpenseTypeObjectCode(getDocumentTypeName(), getTraveler().getTravelerTypeCode(), getTripTypeCode());
}
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.ACTUAL_EXPENSES, null, line));
}
else{
getImportedExpenses().get(index).addExpenseDetails(line);
if (!ObjectUtils.isNull(getTraveler()) && !StringUtils.isBlank(getTraveler().getTravelerTypeCode()) && !StringUtils.isBlank(getTripTypeCode())) {
getImportedExpenses().get(index).refreshExpenseTypeObjectCode(getDocumentTypeName(), getTraveler().getTravelerTypeCode(), getTripTypeCode());
}
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.IMPORTED_EXPENSES, null, line));
}
}
/**
* Adds a new other expense line
*
* @see org.kuali.kfs.module.tem.document.TravelDocument#removeActualExpense(java.lang.Integer)
*/
@Override
@Transient
public void removeActualExpense(final Integer index) {
final ActualExpense line = getActualExpenses().remove(index.intValue());
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.ACTUAL_EXPENSES, line, null));
//Remove detail lines which are associated with parentId
int nextIndex = -1;
while((nextIndex = getNextDetailIndex(line.getId())) !=-1){
final ActualExpense detailLine = getActualExpenses().remove(nextIndex);
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.ACTUAL_EXPENSES, detailLine, null));
}
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#removeExpense(org.kuali.kfs.module.tem.businessobject.TemExpense, java.lang.Integer)
*/
@Override
@Transient
public void removeExpense(TemExpense expense, Integer index) {
TemExpense line = null;
if (expense instanceof ActualExpense){
line = getActualExpenses().remove(index.intValue());
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.ACTUAL_EXPENSES, line, null));
}
else{
line = getImportedExpenses().remove(index.intValue());
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.IMPORTED_EXPENSES, line, null));
}
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#removeExpenseDetail(org.kuali.kfs.module.tem.businessobject.TemExpense, java.lang.Integer)
*/
@Override
@Transient
public void removeExpenseDetail(TemExpense expense, Integer index) {
final TemExpense line = expense.getExpenseDetails().remove(index.intValue());
notifyChangeListeners(new PropertyChangeEvent(this, TemPropertyConstants.EXPENSES_DETAILS, line, null));
}
/**
*
* @param id
* @return
*/
@Transient
private int getNextDetailIndex(Long id){
int index = 0;
for(ActualExpense detailLine: getActualExpenses()){
if(ObjectUtils.isNotNull(detailLine.getExpenseParentId()) && detailLine.getExpenseParentId().equals(id)){
return index;
}
index++;
}
return -1;
}
/**
*
* @return
*/
@Transient
public List<Map<String, KualiDecimal>> getPerDiemExpenseTotals() {
return getTravelDocumentService().calculateDailyTotals(this.perDiemExpenses);
}
/**
*
* @return
*/
public KualiDecimal getRate() {
return rate;
}
/**
*
* @param rate
*/
public void setRate(KualiDecimal rate) {
this.rate = rate;
}
/**
* Gets the disabledProperties attribute.
* @return Returns the disabledProperties.
*/
@Override
public Map<String, String> getDisabledProperties() {
if (disabledProperties == null){
disabledProperties = new HashMap<String, String>();
}
return disabledProperties;
}
/**
* Sets the disabledProperties attribute value.
* @param disabledProperties The disabledProperties to set.
*/
public void setDisabledProperties(Map<String, String> disabledProperties) {
this.disabledProperties = disabledProperties;
}
/**
* Gets the profileId attribute.
* @return Returns the profileId.
*/
@Override
public Integer getProfileId() {
return temProfileId;
}
/**
* Sets the profileId attribute value, looks up the profile and populates the TravelerDetail based on the profile.
* @param profileId The profileId to set.
*/
@Override
public void setProfileId(Integer profileId) {
this.temProfileId = profileId;
Map<String, Object> primaryKeys = new HashMap<String, Object>();
primaryKeys.put(TemPropertyConstants.TemProfileProperties.PROFILE_ID, profileId);
setTemProfile(getBusinessObjectService().findByPrimaryKey(TemProfile.class, primaryKeys));
}
/**
* Gets the temProfileId attribute.
* @return Returns the temProfileId.
*/
@Override
public Integer getTemProfileId() {
return temProfileId;
}
/**
* Sets the temProfileId attribute value.
* @param temProfileId The temProfileId to set.
*/
@Override
public void setTemProfileId(Integer temProfileId) {
this.temProfileId = temProfileId;
}
/**
* Gets the temProfile attribute.
* @return Returns the temProfile.
*/
@Override
public TemProfile getTemProfile() {
return temProfile;
}
/**
* Sets the temProfile attribute value and populates the TravelerDetail from the TemProfile.
* @param temProfile The temProfile to set.
*/
@Override
public void setTemProfile(TemProfile temProfile) {
this.temProfile = temProfile;
if(temProfile != null){
getTravelerService().populateTemProfile(temProfile);
if (temProfile.getTravelerType() == null){
Map<String, Object> fieldValues = new HashMap<String, Object>();
fieldValues.put(KFSPropertyConstants.CODE, temProfile.getTravelerTypeCode());
List<TravelerType> types = (List<TravelerType>) getBusinessObjectService().findMatching(TravelerType.class, fieldValues);
temProfile.setTravelerType(types.get(0));
setTemProfileId(temProfile.getProfileId());
}
if (traveler == null) {
setTraveler(new TravelerDetail());
}
traveler.setDocumentNumber(this.documentNumber);
getTravelerService().convertTemProfileToTravelerDetail(temProfile,(traveler == null?new TravelerDetail():traveler));
}
}
/**
*
* This method sets up the traveler from the TravelerDetail if it exists,
* otherwise it populates the TravelerDetail from the TemProfile.
* @param temProfileId
* @param traveler
*/
public void configureTraveler(Integer temProfileId, TravelerDetail traveler) {
if (traveler != null && traveler.getId() != null) {
// There's a traveler, which means it needs to copy the traveler, rather than
// setting it up from the profile, which is why setProfileId() is not called here.
this.temProfileId = temProfileId;
Map<String, Object> primaryKeys = new HashMap<String, Object>();
primaryKeys.put(TemPropertyConstants.TemProfileProperties.PROFILE_ID, temProfileId);
this.temProfile = getBusinessObjectService().findByPrimaryKey(TemProfile.class, primaryKeys);
this.traveler = getTravelerService().copyTravelerDetail(traveler, this.documentNumber);
}
else {
setProfileId(temProfileId);
}
}
/**
*
* This method wraps {@link #checkTravelerFieldForChanges(String, String, String, String)} for looping purposes.
* @param o1
* @param o2
* @param propertyName
* @param noteText
* @param fieldLabel
* @return
*/
private String checkTravelerFieldForChanges(Object o1, Object o2, String propertyName, String noteText, String fieldLabel){
String profileFieldValue = (String) ObjectUtils.getPropertyValue(o1, propertyName);
String travelerDetailFieldValue = (String) ObjectUtils.getPropertyValue(o2, propertyName);
return checkTravelerFieldForChanges(profileFieldValue != null ? profileFieldValue.trim() : "", travelerDetailFieldValue != null ? travelerDetailFieldValue.trim() : "", noteText, fieldLabel);
}
/**
*
* This method compares profile and traveler field values using {@link StringValueComparator#compare(Object, Object)} and returns the formatted noteText accordingly
* @param profileFieldValue
* @param travelerDetailFieldValue
* @param noteText
* @param fieldLabel
* @return
*/
private String checkTravelerFieldForChanges(String profileFieldValue, String travelerDetailFieldValue, String noteText, String fieldLabel) {
if(StringValueComparator.getInstance().compare(profileFieldValue, travelerDetailFieldValue) == 0){
return noteText;
}
return noteText += fieldLabel + ", " ;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getDelinquentAction()
*/
@Override
public String getDelinquentAction(){
if(tripEnd != null){
Collection<String> delinquentRules = getParameterService().getParameterValuesAsString(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.NUMBER_OF_DAYS_DELINQUENT);
String action = null;
if(delinquentRules != null){
for(String rule : delinquentRules){
String[] arg = rule.split("=");
if(arg != null && arg.length == 2){
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, Integer.parseInt(arg[1]) * -1);
if(tripEnd.before(cal.getTime())){
if(action != null){
if(TemPropertyConstants.delinquentActionsRank().get(action) < TemPropertyConstants.delinquentActionsRank().get(arg[0])){
action = arg[0];
}
}else{
action = arg[0];
}
}
}
}
}
return action;
}
return null;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#canDisplayAgencySitesUrl()
*/
@Override
public boolean canDisplayAgencySitesUrl(){
String value = getConfigurationService().getPropertyValueAsString(ENABLE_AGENCY_SITES_URL);
if(value== null || value.length() ==0 || !value.equalsIgnoreCase("Y")){
return false;
}
return true;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getAgencySitesUrl()
*/
@Override
public String getAgencySitesUrl(){
return getConfigurationService().getPropertyValueAsString(AGENCY_SITES_URL);
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#canPassTripIdToAgencySites()
*/
@Override
public boolean canPassTripIdToAgencySites(){
String value = getConfigurationService().getPropertyValueAsString(PASS_TRIP_ID_TO_AGENCY_SITES);
if(value== null || value.length() ==0 || !value.equalsIgnoreCase("Y")){
return false;
}
return true;
}
/**
* @see org.kuali.rice.kns.document.DocumentBase#buildListOfDeletionAwareLists()
*/
@SuppressWarnings("rawtypes")
@Override
public List buildListOfDeletionAwareLists() {
List managedLists = super.buildListOfDeletionAwareLists();
managedLists.add(getImportedExpenses());
managedLists.add(getPerDiemExpenses());
managedLists.add(getGroupTravelers());
managedLists.add(getAllActualExpensesWithDetails());
return managedLists;
}
/**
* Get all Actual expenses with details
*
* If expenses have details, use them too
*
* @return
*/
public List<TemExpense> getAllActualExpensesWithDetails(){
List<TemExpense> expenseList = new ArrayList<TemExpense>();
if (ObjectUtils.isNotNull(getActualExpenses())) {
for (ActualExpense ae : getActualExpenses()) {
expenseList.add(ae);
if (!ae.getExpenseDetails().isEmpty()) {
expenseList.addAll(ae.getExpenseDetails());
}
}
}
return expenseList;
}
/**
*
* @return
*/
@Override
public Boolean getDelinquentTRException() {
return delinquentTRException != null ? delinquentTRException : false;
}
/**
*
* @param delinquentTRException
*/
public void setDelinquentTRException(Boolean delinquentTRException) {
this.delinquentTRException = delinquentTRException;
}
/**
* Gets the blanketTravel attribute.
*
* @return Returns the blanketTravel
*/
@Override
public Boolean getBlanketTravel() {
return blanketTravel;
}
/**
* Gets the blanketTravel attribute.
*
* @return Returns the blanketTravel
*/
public Boolean isBlanketTravel() {
return getBlanketTravel();
}
/**
* Sets the blanketTravel attribute.
*
* @param blanketTravel The blanketTravel to set.
*/
public void setBlanketTravel(Boolean blanketTravel) {
this.blanketTravel = blanketTravel;
}
/**
*
* @param argTaxSelectable
*/
public void setTaxSelectable(Boolean argTaxSelectable){
this.taxSelectable = argTaxSelectable;
}
/**
*
* @return
*/
public Boolean getTaxSelectable(){
return taxSelectable;
}
/**
*
* @return
*/
public Boolean isTaxSelectable(){
return taxSelectable;
}
/**
*
* @return
*/
protected boolean requiresAccountApprovalRouting() {
KualiDecimal total = KualiDecimal.ZERO;
for (AccountingLine line : ((List<AccountingLine>) this.getSourceAccountingLines())) {
if (line.getAmount().isGreaterThan(KualiDecimal.ZERO)) {
return true;
}
}
return false;
}
/**
*
* @return
*/
protected boolean requiresDivisionApprovalRouting() {
return (getTravelDocumentService().getTotalAuthorizedEncumbrance(this).isGreaterEqual(new KualiDecimal(getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.CUMULATIVE_REIMBURSABLE_AMOUNT_WITHOUT_DIVISION_APPROVAL))));
}
/**
*
* @return
*/
protected boolean requiresInternationalTravelReviewRouting() {
if (ObjectUtils.isNotNull(this.getTripTypeCode()) && getParameterService().getParameterValuesAsString(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.INTERNATIONAL_TRIP_TYPES).contains(this.getTripTypeCode())) {
return true;
}
return false;
}
/**
* Tax Manager approval is required if any actual expenses or its detail is taxable
*
* @return
*/
protected boolean requiresTaxManagerApprovalRouting() {
for (TemExpense line : getAllActualExpensesWithDetails()) {
if(line.getTaxable()){
return true;
}
}
for (TemExpense imported : getImportedExpenses()) {
if (imported.getTaxable()) {
return true;
}
}
return false;
}
/**
*
* @return
*/
protected boolean requiresSeparationOfDutiesRouting(){
String code = getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.SEPARATION_OF_DUTIES_ROUTING_CHOICE);
if (code.equals(TemConstants.SEP_OF_DUTIES_FO)){
if (!requiresAccountApprovalRouting()){
return false;
}
if (getTraveler().getPrincipalId() != null){
return getTravelDocumentService().isResponsibleForAccountsOn(this, this.getTraveler().getPrincipalId());
}
}
else if (code.equals(TemConstants.SEP_OF_DUTIES_DR)){
if (!requiresDivisionApprovalRouting()){
return false;
}
if (getTraveler().getPrincipalId() != null){
RoleService service = SpringContext.getBean(RoleService.class);
List<String> principalIds = (List<String>) service.getRoleMemberPrincipalIds(TemConstants.PARAM_NAMESPACE, TemConstants.TemRoleNames.DIVISION_REVIEWER, null);
for (String id : principalIds){
if (id.equals(getTraveler().getPrincipalId())){
return true;
}
}
}
}
return false;
}
/**
* Check all actual expenses and details
*
* @return
*/
protected boolean requiresSpecialRequestReviewRouting() {
for (TemExpense expense: getAllActualExpensesWithDetails()) {
if (checkActualExpenseSpecialRequest(expense)) {
return true;
}
}
return false;
}
/**
*
* @param expense
* @return
*/
private boolean checkActualExpenseSpecialRequest(TemExpense expense) {
//check class of service for this expense
if (StringUtils.isNotEmpty(expense.getClassOfServiceCode())){
Map<String, String> searchMap = new HashMap<String, String>();
searchMap.put(KRADPropertyConstants.CODE, expense.getClassOfServiceCode());
ClassOfService classOfService = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(ClassOfService.class, searchMap);
if (classOfService.isApprovalRequired()) {
return true;
}
}
//check if the expense type code requires special request
ExpenseTypeObjectCode expenseTypeCode = expense.getExpenseTypeObjectCode();
if (expenseTypeCode.isSpecialRequestRequired()) {
return true;
}
if (expense.isRentalCar() && expense.getRentalCarInsurance()){
return true;
}
return false;
}
protected boolean isBudgetReviewRequired() {
// if document's fiscal year is less than or equal to the current fiscal year
final UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class);
final GeneralLedgerPendingEntryService generalLedgerPendingEntryService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
final SufficientFundsService sufficientFundsService = SpringContext.getBean(SufficientFundsService.class);
final BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
Integer fiscalYear = universityDateService.getCurrentFiscalYear();
if (getTripEnd() != null) {
Calendar tripEndCal = Calendar.getInstance();
tripEndCal.setTimeInMillis(getTripEnd().getTime());
fiscalYear = new Integer(tripEndCal.get(Calendar.YEAR));
}
if (universityDateService.getCurrentFiscalYear().compareTo(fiscalYear) >= 0) {
// delete and recreate the GL entries for this document so they do not get included in the SF check
// This is *NOT* ideal. The SF service needs to be updated to allow it to provide the current
// document number so that it can be exlcuded from pending entry checks.
List<GeneralLedgerPendingEntry> pendingEntries = getPendingLedgerEntriesForSufficientFundsChecking();
// dumb loop to just force OJB to load the objects. Otherwise, the proxy object above
// only gets resolved *after* the delete below and no SF check happens.
for ( GeneralLedgerPendingEntry glpe : pendingEntries ) {
glpe.getChartOfAccountsCode();
}
generalLedgerPendingEntryService.delete(getDocumentNumber());
// get list of sufficientfundItems
List<SufficientFundsItem> fundsItems = sufficientFundsService.checkSufficientFunds(getPendingLedgerEntriesForSufficientFundsChecking());
generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(this);
businessObjectService.save( getGeneralLedgerPendingEntries() );
//kfsmi-7289
if (fundsItems != null && fundsItems.size() > 0) {
return true;
}
}
return false;
}
/**
* Gets the groupTravelers attribute.
*
* @return Returns the groupTravelers.
*/
@Override
public List<GroupTraveler> getGroupTravelers() {
Collections.sort(groupTravelers, new GroupTravelerComparator());
return groupTravelers;
}
/**
* Sets the groupTravelers attribute value.
*
* @param groupTravelers The groupTravelers to set.
*/
@Override
public void setGroupTravelers(List<GroupTraveler> groupTravelers) {
this.groupTravelers = groupTravelers;
}
/**
* This method adds a new group traveler line to the travel doc
*
* @param group traveler line
*/
@Override
public void addGroupTravelerLine(GroupTraveler line) {
line.setFinancialDocumentLineNumber(this.groupTravelers.size() + 1);
line.setDocumentNumber(this.documentNumber);
this.groupTravelers.add(line);
}
/**
* Given the <code>financialObjectCode</code>, determine the total of the {@link SourceAccountingLine} instances with that
* <code>financialObjectCode</code>
*
* @param financialObjectCode to search for total on
* @return @{link KualiDecimal} with total value for {@link AccountingLines} with <code>finanncialObjectCode</code>
*/
@Override
public KualiDecimal getTotalFor(final String financialObjectCode) {
KualiDecimal retval = KualiDecimal.ZERO;
LOG.debug("Getting total for " + financialObjectCode);
for (final AccountingLine line : (List<AccountingLine>) getSourceAccountingLines()) {
try {
LOG.debug("Comparing "+ financialObjectCode+ " to "+ line.getObjectCode().getCode());
if (line.getObjectCode().getCode().equals(financialObjectCode)) {
retval = retval.add(line.getAmount());
}
}
catch(Exception e){
e.printStackTrace();
}
}
return retval;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getNonReimbursableTotal()
*/
@Override
public KualiDecimal getNonReimbursableTotal() {
KualiDecimal total = KualiDecimal.ZERO;
for (ExpenseType expense : EnumSet.allOf(ExpenseType.class)){
total = getTravelExpenseService().getExpenseServiceByType(expense).getNonReimbursableExpenseTotal(this).add(total);
}
return total;
}
/**
* This method returns total expense amount minus the non-reimbursable
*
* @return
*/
@Override
public KualiDecimal getApprovedAmount() {
KualiDecimal total = KualiDecimal.ZERO;
for (ExpenseType expense : EnumSet.allOf(ExpenseType.class)){
total = getTravelExpenseService().getExpenseServiceByType(expense).getAllExpenseTotal(this, false).add(total);
}
return total;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getMealWithoutLodgingReason()
*/
@Override
public String getMealWithoutLodgingReason() {
return mealWithoutLodgingReason;
}
/**
*
* @param mealWithoutLodgingReason
*/
public void setMealWithoutLodgingReason(String mealWithoutLodgingReason) {
this.mealWithoutLodgingReason = mealWithoutLodgingReason;
}
/**
* Determines if there are any meal expenses without lodging expenses on the same day
* @param perDiemExpenses per diem expenses to check
* @param actualExpenses actual expenses to check
* @return true if there are meals charged for any day of the trip without lodging; false otherwise
*/
public boolean isMealsWithoutLodging() {
Set<java.sql.Date> daysWithMeals = getDaysWithMeals(getPerDiemExpenses(), getActualExpenses());
if (KfsDateUtils.isSameDay(getTripBegin(), getTripEnd()) && !daysWithMeals.isEmpty()) {
return true;
}
for (java.sql.Date dayWithMeal : daysWithMeals) {
if (!hasLodgingExpenseOnDay(dayWithMeal, getPerDiemExpenses(), getActualExpenses())) {
if (getTripEnd() == null || (getTripEnd() != null && !KfsDateUtils.isSameDay(getTripEnd(), dayWithMeal))) {
return true;
}
}
}
return false;
}
/**
* Finds all of the days within per diem expenses and actual expenses which have meals
* @param perDiemExpenses the per diem expenses to check
* @param actualExpenses the actual expenses to check
* @return a Set of days to check
*/
protected Set<java.sql.Date> getDaysWithMeals(List<PerDiemExpense> perDiemExpenses, List<ActualExpense> actualExpenses) {
Set<java.sql.Date> days = new HashSet<java.sql.Date>();
for (PerDiemExpense perDiemExpense : perDiemExpenses) {
if (perDiemExpense.getBreakfastValue().isGreaterThan(KualiDecimal.ZERO) || perDiemExpense.getLunchValue().isGreaterThan(KualiDecimal.ZERO) || perDiemExpense.getDinnerValue().isGreaterThan(KualiDecimal.ZERO)) {
java.sql.Date day = new java.sql.Date(perDiemExpense.getMileageDate().getTime());
days.add(day);
}
}
for (ActualExpense expense : actualExpenses) {
if (expense.isBreakfast() || expense.isLunch() || expense.isDinner()) {
days.add(expense.getExpenseDate());
}
}
return days;
}
/**
* Looks through both per diem expenses and actual expenses to see if there is a lodging (or, in the case of actual expense, lodging allowance) expense for the given day
* @param day the day to find a lodging expense for
* @param perDiemExpenses the per diem expenses to look through
* @param actualExpenses the actual expenses to look through
* @return true if a lodging expense is found, false otherwise
*/
protected boolean hasLodgingExpenseOnDay(java.sql.Date day, List<PerDiemExpense> perDiemExpenses, List<ActualExpense> actualExpenses) {
for (PerDiemExpense perDiemExpense : perDiemExpenses) {
java.sql.Date perDiemDate = new java.sql.Date(perDiemExpense.getMileageDate().getTime());
if (KfsDateUtils.isSameDay(day, perDiemDate) && perDiemExpense.getLodging().isGreaterThan(KualiDecimal.ZERO)) {
return true;
}
}
for (ActualExpense expense : actualExpenses) {
if (KfsDateUtils.isSameDay(day, expense.getExpenseDate()) && (expense.isLodging() || expense.isLodgingAllowance())) {
return true;
}
}
return false;
}
/**
* @return the bank code which payments made to reimburse this document will be drawn from
*/
public String getFinancialDocumentBankCode() {
return financialDocumentBankCode;
}
/**
* Sets the bank code which payments made to reimburse this document will be drawn from
* @param financialDocumentBankCode the bank code which payments made to reimburse this document will be drawn from
*/
public void setFinancialDocumentBankCode(String financialDocumentBankCode) {
this.financialDocumentBankCode = financialDocumentBankCode;
}
/**
* @return the full bank business object for the bank from which payments made to reimburse this document will be drawn
*/
public Bank getBank() {
return bank;
}
/**
* Sets the full bank business object for the bank from which payments made to reimburse this document will be drawn
* @param bank the full bank business object for the bank from which payments made to reimburse this document will be drawn
*/
public void setBank(Bank bank) {
this.bank = bank;
}
/**
* Sets the bank code for a new document to the setup default for the TEM document.
*/
public void setDefaultBankCode() {
Bank defaultBank = SpringContext.getBean(BankService.class).getDefaultBankByDocType(this.getClass());
if (defaultBank != null) {
this.financialDocumentBankCode = defaultBank.getBankCode();
this.bank = defaultBank;
}
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getTransportationModes()
*/
@Override
public List<TransportationModeDetail> getTransportationModes() {
return null;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setTransportationModes(java.util.List)
*/
@Override
public void setTransportationModes(List<TransportationModeDetail> transportationModes) {
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getImportedExpenses()
*/
@Override
public List<ImportedExpense> getImportedExpenses() {
return importedExpenses;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setImportedExpenses(java.util.List)
*/
@Override
public void setImportedExpenses(List<ImportedExpense> importedExpenses) {
this.importedExpenses = importedExpenses;
}
@Override
public boolean isTripProgenitor() {
return tripProgenitor;
}
public void setTripProgenitor(boolean tripProgenitor) {
this.tripProgenitor = tripProgenitor;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getCTSTotal()
*/
@Override
public KualiDecimal getCTSTotal() {
KualiDecimal lessCtsCharges = getTravelExpenseService().getExpenseServiceByType(ExpenseType.importedCTS).getAllExpenseTotal(this, false);
return lessCtsCharges;
}
/**
* @return returns complete CTS charges, including non-reimbursable charges
*/
public KualiDecimal getFullCTSTotal() {
KualiDecimal lessCtsCharges = getTravelExpenseService().getExpenseServiceByType(ExpenseType.importedCTS).getAllExpenseTotal(this, true);
return lessCtsCharges;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getCorporateCardTotal()
*/
@Override
public KualiDecimal getCorporateCardTotal() {
KualiDecimal lessCorpCardCharges = getTravelExpenseService().getExpenseServiceByType(ExpenseType.importedCorpCard).getAllExpenseTotal(this, false);
return lessCorpCardCharges;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getHistoricalTravelExpenses()
*/
@Override
public List<HistoricalTravelExpense> getHistoricalTravelExpenses() {
Map<String,String> fieldValues = new HashMap<String, String>();
fieldValues.put(TemPropertyConstants.TRIP_ID,this.getTravelDocumentIdentifier());
historicalTravelExpenses = (List<HistoricalTravelExpense>) getBusinessObjectService().findMatchingOrderBy(HistoricalTravelExpense.class, fieldValues, TemPropertyConstants.TRANSACTION_POSTING_DATE, true);
for (HistoricalTravelExpense historicalTravelExpense : historicalTravelExpenses){
historicalTravelExpense.refreshReferenceObject(TemPropertyConstants.CREDIT_CARD_AGENCY);
historicalTravelExpense.refreshReferenceObject(TemPropertyConstants.AGENCY_STAGING_DATA);
historicalTravelExpense.refreshReferenceObject(TemPropertyConstants.CREDIT_CARD_STAGING_DATA);
historicalTravelExpense.getCreditCardAgency().refreshReferenceObject(TemPropertyConstants.TRAVEL_CARD_TYPE);
}
return historicalTravelExpenses;
}
/**
* @return HistoricalTravelExpense records associated with the trip which are reconciled - ie, they have both agency staging data and credit card staging data associated
*/
public List<HistoricalTravelExpense> getReconciledHistoricalTravelExpenses() {
List<HistoricalTravelExpense> allHistoricalExpenses = getHistoricalTravelExpenses();
List<HistoricalTravelExpense> reconciledHistoricalExpenses = new ArrayList<HistoricalTravelExpense>();
for (HistoricalTravelExpense historicalExpense : allHistoricalExpenses) {
if (StringUtils.equals(historicalExpense.getReconciled(), TemConstants.ReconciledCodes.RECONCILED)) {
reconciledHistoricalExpenses.add(historicalExpense);
}
}
return reconciledHistoricalExpenses;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#setHistoricalTravelExpenses(java.util.List)
*/
@Override
public void setHistoricalTravelExpenses(List<HistoricalTravelExpense> historicalTravelExpenses) {
this.historicalTravelExpenses = historicalTravelExpenses;
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#isDebit(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
*/
@Override
public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
return false;
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#getGeneralLedgerPendingEntrySourceDetails()
*/
@Override
public List<GeneralLedgerPendingEntrySourceDetail> getGeneralLedgerPendingEntrySourceDetails() {
List<GeneralLedgerPendingEntrySourceDetail> accountingLines = new ArrayList<GeneralLedgerPendingEntrySourceDetail>();
if (getSourceAccountingLines() != null) {
for (TemSourceAccountingLine line : (List<TemSourceAccountingLine>)getSourceAccountingLines()){
//we are generating source accounting lines on document's expense type code
// could be either OUT OF POCKET or ENCUMBRANCE
if (line.getCardType().equals(getDefaultCardTypeCode())){
accountingLines.add(line);
}
}
}
return accountingLines;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#initiateDocument()
*/
@Override
public void initiateDocument() {
//pre filled descriptions
getDocumentHeader().setDocumentDescription(TemConstants.PRE_FILLED_DESCRIPTION);
Person currentUser = GlobalVariables.getUserSession().getPerson();
if (!getTemRoleService().isTravelArranger(currentUser)) {
TemProfile temProfile = getTemProfileService().findTemProfileByPrincipalId(currentUser.getPrincipalId());
if (temProfile != null) {
setTemProfile(temProfile);
}
}
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getEncumbranceTotal()
*/
@Override
public KualiDecimal getEncumbranceTotal() {
return KualiDecimal.ZERO;
}
/**
* @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange(org.kuali.rice.kew.dto.DocumentRouteStatusChange)
*/
@Override
public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
this.refreshNonUpdateableReferences();
SpringContext.getBean(TravelDocumentNotificationService.class).sendNotificationOnChange(this, statusChangeEvent);
super.doRouteStatusChange(statusChangeEvent);
if (getDocumentHeader().getWorkflowDocument().isProcessed()) {
getTravelExpenseService().getExpenseServiceByType(ExpenseType.importedCTS).updateExpense(this);
getTravelExpenseService().getExpenseServiceByType(ExpenseType.importedCorpCard).updateExpense(this);
}
LOG.debug("Handling Route Status changing to [" + statusChangeEvent.getNewRouteStatus() + "]");
//Update internal app doc status status
String currStatus = getDocumentHeader().getWorkflowDocument().getApplicationDocumentStatus();
if (ObjectUtils.isNotNull(currStatus)) {
try {
updateAndSaveAppDocStatus(currStatus);
}
catch (WorkflowException ex) {
throw new RuntimeException("Could not update application document status", ex);
}
}
//for disapproval cases
if (DocumentStatus.DISAPPROVED.getCode().equals(statusChangeEvent.getNewRouteStatus())) {
try {
updateAndSaveAppDocStatus(getDisapprovedAppDocStatusMap().get(getAppDocStatus()));
}
catch (WorkflowException ex) {
throw new RuntimeException("Could not update application document status on document disapproval", ex);
}
}
//for cancel cases
if (DocumentStatus.CANCELED.getCode().equals(statusChangeEvent.getNewRouteStatus())) {
try {
updateAndSaveAppDocStatus(TemConstants.TravelStatusCodeKeys.CANCELLED);
}
catch (WorkflowException we) {
throw new RuntimeException("Could not update application document status on document disapproval", we);
}
}
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#generateDocumentGeneralLedgerPendingEntries(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
*/
@Override
public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
getTravelExpenseService().getExpenseServiceByType(ExpenseType.importedCTS).processExpense(this, sequenceHelper);
getTravelExpenseService().getExpenseServiceByType(ExpenseType.importedCorpCard).processExpense(this, sequenceHelper);
return true;
}
@Override
public String getDocumentTypeName(){
return this.getDataDictionaryEntry().getDocumentTypeName();
}
public boolean canPayDVToVendor() {
return (getDocumentHeader() != null && !(getDocumentHeader().getWorkflowDocument().isCanceled() || getDocumentHeader().getWorkflowDocument().isInitiated() || getDocumentHeader().getWorkflowDocument().isException() || getDocumentHeader().getWorkflowDocument().isDisapproved() || getDocumentHeader().getWorkflowDocument().isSaved()));
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#populateVendorPayment(org.kuali.kfs.fp.document.DisbursementVoucherDocument)
*/
@Override
public void populateVendorPayment(DisbursementVoucherDocument disbursementVoucherDocument) {
disbursementVoucherDocument.getDocumentHeader().setDocumentDescription("Created by " + this.getDocumentTypeName() + " document" + (this.getTravelDocumentIdentifier() == null?".":": " + this.getTravelDocumentIdentifier()));
disbursementVoucherDocument.getDocumentHeader().setOrganizationDocumentNumber(this.getTravelDocumentIdentifier());
String reasonCode = getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.VENDOR_PAYMENT_REASON_CODE);
disbursementVoucherDocument.getDvPayeeDetail().setDisbVchrPaymentReasonCode(reasonCode);
disbursementVoucherDocument.getDocumentHeader().setOrganizationDocumentNumber(this.getTravelDocumentIdentifier());
Calendar dueDate = Calendar.getInstance();
dueDate.add(Calendar.DATE, 1);
disbursementVoucherDocument.setDisbursementVoucherDueDate(new java.sql.Date(dueDate.getTimeInMillis()));
Person initiator = SpringContext.getBean(PersonService.class).getPerson(this.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId());
if (initiator == null) {
throw new RuntimeException("Initiator could not be found in KIM! Which is super-strange...this block should never execute in reality"); // because KIM wouldn't even let you here, would it?
}
disbursementVoucherDocument.setDisbVchrContactPersonName(initiator.getPrincipalName());
disbursementVoucherDocument.setDisbVchrContactPhoneNumber(initiator.getPhoneNumber());
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getReimbursableSourceAccountingLines()
*/
@Override
public List<SourceAccountingLine> getReimbursableSourceAccountingLines() {
List<SourceAccountingLine> reimbursableLines = new ArrayList<SourceAccountingLine>();
for (TemSourceAccountingLine line : (List<TemSourceAccountingLine>) getSourceAccountingLines()){
if (TemConstants.ACTUAL_EXPENSE.equals(line.getCardType())){
reimbursableLines.add(line);
}
}
return reimbursableLines;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#getDefaultAccountingLineCardAgencyType()
*/
@Override
public String getDefaultAccountingLineCardAgencyType(){
return getDefaultCardTypeCode();
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#hasCustomDVDistribution()
*/
@Override
public boolean hasCustomDVDistribution(){
return false;
}
/**
* Check trip type to determine if it should be generating encumbrance / dis-encumbrance
*
* @return
*/
public boolean isTripGenerateEncumbrance(){
return getTripType() != null && getTripType().isGenerateEncumbrance() && !hasOnlyPrepaidExpenses();
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#isSpecialCircumstancesDefaultOpen()
*/
@Override
public boolean isSpecialCircumstancesDefaultOpen(){
boolean isOpen = false;
if (expenseLimit != null){
isOpen = true;
}else{
//open if any of the checkbox is selected (but not for text)
for (SpecialCircumstances circumstance : getSpecialCircumstances()){
if (circumstance.getResponse()){
isOpen = true;
break;
}
}
}
return isOpen;
}
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#isSpecialCircumstancesDefaultOpen()
*/
@Override
public boolean isEmergencyContactDefaultOpen(){
boolean isOpen = false;
Collection<String> internationalTrips = getParameterService().getParameterValuesAsString(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.INTERNATIONAL_TRIP_TYPES);
if (internationalTrips.contains(getTripTypeCode())){
isOpen = traveler.getEmergencyContacts().isEmpty();
}
return isOpen;
}
/**
* Should be defined per each doc type
*
* @see org.kuali.kfs.module.tem.document.TravelDocument#getDisapprovedAppDocStatusMap()
*/
@Override
public abstract Map<String, String> getDisapprovedAppDocStatusMap();
/**
* @see org.kuali.kfs.module.tem.document.TravelDocument#isTravelAuthorizationDoc()
*/
@Override
public boolean isTravelAuthorizationDoc(){
return TravelDocTypes.getAuthorizationDocTypes().contains(getDocumentTypeName());
}
/**
* Determines if this document should attempt to refresh the expense type object codes for expenses or not
* @return true of expense type object codes on expenses should be refreshed; false otherwise
*/
@Override
public boolean shouldRefreshExpenseTypeObjectCode() {
if (!StringUtils.isBlank(getDocumentHeader().getDocumentNumber()) && !ObjectUtils.isNull(getDocumentHeader()) && getDocumentHeader().getWorkflowDocument() != null) {
if (getDocumentHeader().getWorkflowDocument().isInitiated() || getDocumentHeader().getWorkflowDocument().isSaved()) {
// can we refresh the expenses? For that to occur, we need trip and traveler type set
return !ObjectUtils.isNull(getTraveler()) && !StringUtils.isBlank(getTraveler().getTravelerTypeCode()) && !StringUtils.isBlank(getTripTypeCode());
}
}
return false;
}
/**
* Refreshes expense type object code values for actual and imported expenses on the document
*/
@Override
public void refreshExpenseTypeObjectCodesForExpenses() {
for (ActualExpense expense : getActualExpenses()) {
expense.refreshExpenseTypeObjectCode(getDocumentTypeName(), getTraveler().getTravelerTypeCode(), getTripTypeCode());
}
for (ImportedExpense expense : getImportedExpenses()) {
expense.refreshExpenseTypeObjectCode(getDocumentTypeName(), getTraveler().getTravelerTypeCode(), getTripTypeCode());
}
}
/**
* Determines if the given per diem expense is on the trip begin date
* @param perDiemExpense the per diem expense to check
* @return true if the per diem expense is on the trip begin date, false otherwise
*/
@Override
public boolean isOnTripBegin(PerDiemExpense perDiemExpense) {
if (getTripBegin() == null || perDiemExpense.getMileageDate() == null) {
return false;
}
return KfsDateUtils.isSameDay(getTripBegin(), perDiemExpense.getMileageDate());
}
/**
* Determines if the given per diem expense is on the trip end date
* @param perDiemExpense the per diem expense to check
* @return true if the per diem expense is on the trip end date, false otherwise
*/
@Override
public boolean isOnTripEnd(PerDiemExpense perDiemExpense) {
if (getTripEnd() == null || perDiemExpense.getMileageDate() == null) {
return false;
}
return KfsDateUtils.isSameDay(getTripEnd(), perDiemExpense.getMileageDate());
}
/**
* @return the amount on the document that needs to be matched by accounting lines - which means we need to remove the hung expenses amount
*/
@Override
public KualiDecimal getTotalAccountLineAmount() {
KualiDecimal approvedAmount = getApprovedAmount();
// remove hung expenses amount
KualiDecimal totalAccountingLineAmount = new KualiDecimal(approvedAmount.bigDecimalValue());
for (HistoricalTravelExpense expense : getHistoricalTravelExpenses()) {
totalAccountingLineAmount = totalAccountingLineAmount.subtract(expense.getConvertedAmount());
}
return totalAccountingLineAmount;
}
/**
* Applies the expense limit to the given amount - that is, if the expense limit exists and is less than the given amount, the expense limit
* is returned, otherwise the expense limit
* @param the amount to check against the expense limit
* @return if the expense limit exists and is less than the given amount, returns the expense limit; else returns the given amount
*/
@Override
public KualiDecimal applyExpenseLimit(KualiDecimal totalAmount) {
if (getExpenseLimit() == null || !getExpenseLimit().isPositive()) {
return totalAmount;
}
if (getExpenseLimit().isGreaterEqual(totalAmount)) {
return totalAmount;
}
return getExpenseLimit();
}
public String getTotalDollarAmountForRouting() {
if (!shouldRouteByProfileAccount()) {
return "";
} else {
return getTotalDollarAmount().toString();
}
}
public String getProfileChartForRouting() {
if (!shouldRouteByProfileAccount()) {
return "";
} else {
if (ObjectUtils.isNotNull(getTemProfile())) {
return getTemProfile().getDefaultChartCode();
}
return "";
}
}
public String getProfileAccountForRouting() {
if (!shouldRouteByProfileAccount()) {
return "";
} else {
if (ObjectUtils.isNotNull(getTemProfile())) {
return getTemProfile().getDefaultAccount();
}
return "";
}
}
/**
* Determines if this document should attempt to route by profile account or not
*/
protected abstract boolean shouldRouteByProfileAccount();
/**
* Determines if this document's actual expenses are all pre-paid expenses
* @return true if all expenses on this document are prepaid, false otherwise
*/
@Override
public boolean hasOnlyPrepaidExpenses() {
if (getPerDiemExpenses() != null && !getPerDiemExpenses().isEmpty()) {
return false; // we've got per diem expenses
}
if (getImportedExpenses() != null && !getImportedExpenses().isEmpty()) {
for (ImportedExpense expense : getImportedExpenses()) {
if (!expense.getNonReimbursable()) {
return false; // we've got reimbursable imported expenses
}
}
}
if (getActualExpenses() == null || getActualExpenses().isEmpty()) {
return false; // we don't have any expenses at all
}
for (ActualExpense expense : getActualExpenses()) {
if (!StringUtils.isBlank(expense.getExpenseTypeCode()) && ObjectUtils.isNull(expense.getExpenseType())) {
expense.refreshReferenceObject(TemPropertyConstants.EXPENSE_TYPE);
}
if (!ObjectUtils.isNull(expense.getExpenseType()) && !expense.getExpenseType().isPrepaidExpense()) {
return false;
}
}
return true;
}
/**
* The TA, ENT, RELO, and TR - none of them do sufficient funds checking
* @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#documentPerformsSufficientFundsCheck()
*/
@Override
public boolean documentPerformsSufficientFundsCheck() {
return false;
}
}