/*
* 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.maintenance;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.log4j.Logger;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.TemConstants.AgencyStagingDataErrorCodes;
import org.kuali.kfs.module.tem.TemConstants.ExpenseImportTypes;
import org.kuali.kfs.module.tem.batch.service.AgencyDataImportService;
import org.kuali.kfs.module.tem.batch.service.ExpenseImportByTravelerService;
import org.kuali.kfs.module.tem.batch.service.ExpenseImportByTripService;
import org.kuali.kfs.module.tem.businessobject.AgencyStagingData;
import org.kuali.kfs.module.tem.businessobject.CreditCardAgency;
import org.kuali.kfs.module.tem.businessobject.TemProfile;
import org.kuali.kfs.module.tem.businessobject.TripAccountingInformation;
import org.kuali.kfs.module.tem.service.CreditCardAgencyService;
import org.kuali.kfs.module.tem.util.MessageUtils;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.FinancialSystemMaintainable;
import org.kuali.kfs.sys.report.BusinessObjectReportHelper;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.IdentityService;
import org.kuali.rice.kim.api.identity.principal.Principal;
import org.kuali.rice.kns.document.MaintenanceDocument;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.bo.DocumentHeader;
import org.kuali.rice.krad.bo.Note;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.NoteService;
import org.kuali.rice.krad.util.ErrorMessage;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.MessageMap;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Maintainable instance for the travel agency audit maintenance document
*
*/
public class AgencyStagingDataMaintainable extends FinancialSystemMaintainable {
static final Logger LOG = Logger.getLogger(AgencyStagingDataMaintainable.class);
private volatile static AgencyDataImportService agencyDataImportService;
private volatile static ExpenseImportByTravelerService expenseImportByTravelerService;
private volatile static ExpenseImportByTripService expenseImportByTripService;
private volatile static CreditCardAgencyService creditCardAgencyService;
private volatile static DocumentService documentService;
private volatile static DataDictionaryService dataDictionaryService;
private volatile static IdentityService identityService;
private volatile static NoteService noteService;
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterNew(org.kuali.rice.kns.document.MaintenanceDocument, java.util.Map)
*/
@Override
public void processAfterNew(MaintenanceDocument document, Map<String,String[]> parameters) {
super.processAfterNew(document, parameters);
AgencyStagingData agencyData = (AgencyStagingData) getBusinessObject();
agencyData.setManualCreated(true);
//default the import type (probably by trip)
agencyData.setImportBy(ExpenseImportTypes.IMPORT_BY_TRIP);
agencyData.setCreationTimestamp(getDateTimeService().getCurrentTimestamp());
agencyData.setProcessingTimestamp(getDateTimeService().getCurrentTimestamp());
agencyData.setErrorCode(AgencyStagingDataErrorCodes.AGENCY_NO_ERROR);
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterCopy(org.kuali.rice.kns.document.MaintenanceDocument, java.util.Map)
*/
@Override
public void processAfterCopy(MaintenanceDocument document, Map<String,String[]> parameters) {
super.processAfterCopy(document, parameters);
AgencyStagingData agencyData = (AgencyStagingData) getBusinessObject();
agencyData.setManualCreated(true);
agencyData.setCreationTimestamp(getDateTimeService().getCurrentTimestamp());
AgencyStagingDataMaintainable oldMaintainable = (AgencyStagingDataMaintainable)document.getOldMaintainableObject();
//this is not new, so it must be for copy - we will set the Copied From Id
agencyData.setCopiedFromId(((AgencyStagingData)oldMaintainable.getBusinessObject()).getId());
//set TripAccountingInformation primary key and foreign key to null so the maintainance document can handle setting the appropriate key values
if (!agencyData.getTripAccountingInformation().isEmpty()) {
for(TripAccountingInformation account : agencyData.getTripAccountingInformation()) {
account.setId(null);
account.setAgencyStagingDataId(null);
}
}
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.kns.bo.DocumentHeader)
*/
@Override
public void doRouteStatusChange(DocumentHeader documentHeader) {
if (documentHeader.getWorkflowDocument().isProcessed()){
AgencyStagingData agencyStaging = (AgencyStagingData) getBusinessObject();
updateCreditCardAgency(agencyStaging);
//All validations have passed at this point. Set error code to 'OK'.
agencyStaging.setErrorCode(AgencyStagingDataErrorCodes.AGENCY_NO_ERROR);
//after fixing the agency audit record, attempt to move agency data to historical table
List<ErrorMessage> errors = getAgencyDataImportService().processAgencyStagingExpense(agencyStaging, getGeneralLedgerPendingEntrySequenceHelper());
LOG.info("Agency Data Id: "+ agencyStaging.getId() + (errors.isEmpty() ? " was":" was not") +" processed.");
//add a Note if there were errors reconciling or distributing the record
if (!errors.isEmpty()) {
try {
MaintenanceDocument document = (MaintenanceDocument) getDocumentService().getByDocumentHeaderId(documentHeader.getDocumentNumber());
addNoteAfterProcessingAgencyStagingExpense(document, errors);
}
catch (WorkflowException exception) {
LOG.error("Unable to add Note to Document Id: "+ documentHeader.getDocumentNumber(), exception);
LOG.error(getMessageAsString(errors));
}
}
// nota bene: agency staging data object does NOT need to be saved here as the maint doc will save it itself once processing completes
}
super.doRouteStatusChange(documentHeader);
}
protected GeneralLedgerPendingEntrySequenceHelper getGeneralLedgerPendingEntrySequenceHelper() {
Collection<GeneralLedgerPendingEntry> glpes = getAgencyDataImportService().getGeneralLedgerPendingEntriesForDocumentNumber((AgencyStagingData) getBusinessObject());
int maxGLPESequenceValue = 0;
for (GeneralLedgerPendingEntry glpe : glpes) {
if (glpe.getTransactionLedgerEntrySequenceNumber().intValue() > maxGLPESequenceValue) {
maxGLPESequenceValue = glpe.getTransactionLedgerEntrySequenceNumber().intValue();
}
}
maxGLPESequenceValue++;
return new GeneralLedgerPendingEntrySequenceHelper(maxGLPESequenceValue);
}
/**
*
* @param agencyStaging
*/
protected void updateCreditCardAgency(AgencyStagingData agencyStaging){
//update the agency name base on code if provided
CreditCardAgency agency = getCreditCardAgencyService().getCreditCardAgencyByCode(agencyStaging.getCreditCardOrAgencyCode());
if (agency != null){
agencyStaging.setCreditCardAgency(agency);
}
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterEdit(org.kuali.rice.kns.document.MaintenanceDocument, java.util.Map)
*/
@Override
public void processAfterPost(MaintenanceDocument document, Map<String, String[]> parameters) {
updateCreditCardAgency((AgencyStagingData)document.getNewMaintainableObject().getBusinessObject());
super.processAfterPost(document, parameters);
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterEdit(org.kuali.rice.kns.document.MaintenanceDocument, java.util.Map)
*/
@Override
public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> parameters) {
List<ErrorMessage> errorMessages = null;
AgencyStagingData agency = (AgencyStagingData)document.getNewMaintainableObject().getBusinessObject();
if(TemConstants.ExpenseImportTypes.IMPORT_BY_TRIP.equals(agency.getImportBy())) {
errorMessages = getExpenseImportByTripService().validateAgencyData(agency);
}
else if (TemConstants.ExpenseImportTypes.IMPORT_BY_TRAVELLER.equals(agency.getImportBy())) {
errorMessages = getExpenseImportByTravelerService().validateAgencyData(agency);
}
if (errorMessages.isEmpty()) {
agency.setErrorCode(AgencyStagingDataErrorCodes.AGENCY_NO_ERROR);
}
MessageMap messageMap = GlobalVariables.getMessageMap();
for(ErrorMessage message : errorMessages ){
messageMap.putError(KFSConstants.GLOBAL_ERRORS, message.getErrorKey(), message.getMessageParameters());
}
updateCreditCardAgency((AgencyStagingData)document.getNewMaintainableObject().getBusinessObject());
super.processAfterEdit(document, parameters);
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#saveBusinessObject()
*/
@Override
public void saveBusinessObject() {
AgencyStagingData agencyStaging = (AgencyStagingData) getBusinessObject();
if (agencyStaging.isActive()) {
//since it is fixed and submitted, changing the status to OK unless it has already been set to HIS
if (!agencyStaging.getErrorCode().equals(AgencyStagingDataErrorCodes.AGENCY_MOVED_TO_HISTORICAL)) {
agencyStaging.setErrorCode(AgencyStagingDataErrorCodes.AGENCY_NO_ERROR);
}
}
super.saveBusinessObject();
}
/**
* @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#refresh(java.lang.String, java.util.Map, org.kuali.rice.kns.document.MaintenanceDocument)
*/
@Override
public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) {
addProfileReferenceToRefresh(refreshCaller, fieldValues);
super.refresh(refreshCaller, fieldValues, document);
updateFieldsFromProfileRefresh(refreshCaller, fieldValues, document);
}
/**
* Refresh TemProfile reference if we've just looked it up
*
* @param refreshCaller
* @param fieldValues
*/
protected void addProfileReferenceToRefresh(String refreshCaller, Map fieldValues) {
if (StringUtils.isNotEmpty(refreshCaller) && refreshCaller.equals(TemConstants.TEM_PROFILE_LOOKUPABLE)) {
String referencesToRefresh = ""+ fieldValues.get(KRADConstants.REFERENCES_TO_REFRESH)+ KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR + TemPropertyConstants.PROFILE;
fieldValues.put(KRADConstants.REFERENCES_TO_REFRESH, referencesToRefresh);
}
}
/**
* Update Traveler information (name, id, network id) after looking up the Profile
*
* @param refreshCaller
* @param fieldValues
* @param document
*/
protected void updateFieldsFromProfileRefresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) {
if (StringUtils.isNotEmpty(refreshCaller) &&
refreshCaller.equals(TemConstants.TEM_PROFILE_LOOKUPABLE)) {
AgencyStagingDataMaintainable newMaintainable = (AgencyStagingDataMaintainable)document.getNewMaintainableObject();
AgencyStagingData agencyData = (AgencyStagingData)newMaintainable.getBusinessObject();
TemProfile profile = agencyData.getProfile();
if (ObjectUtils.isNotNull(profile)) {
if (StringUtils.isNotEmpty(profile.getEmployeeId())) {
agencyData.setTravelerId(profile.getEmployeeId());
}
else if (StringUtils.isNotEmpty(profile.getCustomerNumber())) {
agencyData.setTravelerId(profile.getCustomerNumber());
}
else {
agencyData.setTravelerId("");
}
agencyData.setTravelerName(profile.getFirstName() +" "+ profile.getLastName());
if (ObjectUtils.isNotNull(profile.getPrincipal())) {
agencyData.setTravelerNetworkId(profile.getPrincipal().getPrincipalName());
}
else {
agencyData.setTravelerNetworkId("");
}
}
}
}
/**
*
* This method trims the descriptionText to 40 characters.
* @param descriptionText
* @return
*/
protected String trimDescription(String descriptionText) {
return StringUtils.substring(descriptionText, 0, 39);
}
protected void addNoteAfterProcessingAgencyStagingExpense(MaintenanceDocument document, List<ErrorMessage> errors) {
Principal kfsSystemUser = getIdentityService().getPrincipalByPrincipalName(KFSConstants.SYSTEM_USER);
String errorText = getMessageAsString(errors);
if (!StringUtils.isEmpty(errorText)) {
//check maxLength on a Note and truncate if necessary
Integer maxLength = getDataDictionaryService().getAttributeMaxLength(Note.class, KRADConstants.NOTE_TEXT_PROPERTY_NAME);
if (errorText.length() > maxLength) {
LOG.warn("Adding a truncated error text to Note due to space limitations. Original text:");
LOG.warn(errorText);
errorText = errorText.substring(0,maxLength);
}
final Note newNote = getDocumentService().createNoteFromDocument(document, errorText);
newNote.setAuthorUniversalIdentifier(kfsSystemUser.getPrincipalId());
document.addNote(newNote);
getNoteService().save(newNote);
}
}
protected String getMessageAsString(List<ErrorMessage> errorMessages){
List<String> messageList = new ArrayList<String>();
for (ErrorMessage error : errorMessages){
messageList.add(MessageUtils.getErrorMessage(error));
}
StrBuilder builder = new StrBuilder();
builder.appendWithSeparators(messageList, BusinessObjectReportHelper.LINE_BREAK);
return builder.toString();
}
public DateTimeService getDateTimeService(){
return SpringContext.getBean(DateTimeService.class);
}
public AgencyDataImportService getAgencyDataImportService() {
if (agencyDataImportService == null) {
agencyDataImportService = SpringContext.getBean(AgencyDataImportService.class);
}
return agencyDataImportService;
}
public ExpenseImportByTravelerService getExpenseImportByTravelerService() {
if (expenseImportByTravelerService == null) {
expenseImportByTravelerService = SpringContext.getBean(ExpenseImportByTravelerService.class);
}
return expenseImportByTravelerService;
}
public ExpenseImportByTripService getExpenseImportByTripService() {
if (expenseImportByTripService == null) {
expenseImportByTripService = SpringContext.getBean(ExpenseImportByTripService.class);
}
return expenseImportByTripService;
}
public CreditCardAgencyService getCreditCardAgencyService() {
if (creditCardAgencyService == null) {
creditCardAgencyService = SpringContext.getBean(CreditCardAgencyService.class);
}
return creditCardAgencyService;
}
public DocumentService getDocumentService() {
if (documentService == null) {
documentService = SpringContext.getBean(DocumentService.class);
}
return documentService;
}
@Override
public DataDictionaryService getDataDictionaryService() {
if (dataDictionaryService == null) {
dataDictionaryService = SpringContext.getBean(DataDictionaryService.class);
}
return dataDictionaryService;
}
public IdentityService getIdentityService() {
if (identityService == null) {
identityService = SpringContext.getBean(IdentityService.class);
}
return identityService;
}
public NoteService getNoteService() {
if (noteService == null) {
noteService = SpringContext.getBean(NoteService.class);
}
return noteService;
}
}