/*
* 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.sys.batch.service.impl;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import org.joda.time.DateTime;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSParameterKeyConstants;
import org.kuali.kfs.sys.batch.AutoDisapproveDocumentsStep;
import org.kuali.kfs.sys.batch.service.AutoDisapproveDocumentsService;
import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.service.FinancialSystemDocumentService;
import org.kuali.kfs.sys.service.ReportWriterService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.parameter.ParameterEvaluator;
import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.doctype.DocumentType;
import org.kuali.rice.kew.api.doctype.DocumentTypeService;
import org.kuali.rice.kew.api.document.DocumentStatus;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.krad.bo.DocumentHeader;
import org.kuali.rice.krad.bo.Note;
import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.NoteService;
import org.kuali.rice.krad.util.ObjectUtils;
import org.springframework.transaction.annotation.Transactional;
/**
* This class implements the AutoDisapproveDocumentsService batch job.
*/
@Transactional
public class AutoDisapproveDocumentsServiceImpl implements AutoDisapproveDocumentsService {
protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AutoDisapproveDocumentsServiceImpl.class);
public static final String WORKFLOW_DOCUMENT_HEADER_ID_SEARCH_RESULT_KEY = "routeHeaderId";
private DocumentService documentService;
private DocumentTypeService documentTypeService;
private DateTimeService dateTimeService;
private ParameterService parameterService;
private NoteService noteService;
private PersonService personService;
private ReportWriterService autoDisapproveErrorReportWriterService;
private FinancialSystemDocumentService financialSystemDocumentService;
/**
* Constructs a AutoDisapproveDocumentsServiceImpl instance
*/
public AutoDisapproveDocumentsServiceImpl() {
}
/**
* Gathers all documents that are in ENROUTE status and auto disapproves them.
* @see org.kuali.kfs.sys.batch.service.autoDisapproveDocumentsInEnrouteStatus#autoDisapproveDocumentsInEnrouteStatus()
*/
@Override
public boolean autoDisapproveDocumentsInEnrouteStatus() {
boolean success = true ;
if (systemParametersForAutoDisapproveDocumentsJobExist()) {
if (canAutoDisapproveJobRun()) {
LOG.debug("autoDisapproveDocumentsInEnrouteStatus() started");
Person systemUser = getPersonService().getPersonByPrincipalName(KFSConstants.SYSTEM_USER);
String principalId = systemUser.getPrincipalId();
String annotationForAutoDisapprovalDocument = getParameterService().getParameterValueAsString(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_ANNOTATION);
Date documentCompareDate = getDocumentCompareDateParameter();
success = processAutoDisapproveDocuments(principalId, annotationForAutoDisapprovalDocument, documentCompareDate);
}
}
return success;
}
/**
* This method checks if the System parameters have been set up for this batch job.
* @result return true if the system parameters exist, else false
*/
protected boolean systemParametersForAutoDisapproveDocumentsJobExist() {
LOG.debug("systemParametersForAutoDisapproveDocumentsJobExist() started.");
boolean systemParametersExists = true;
systemParametersExists &= checkIfRunDateParameterExists();
systemParametersExists &= checkIfParentDocumentTypeParameterExists();
systemParametersExists &= checkIfDocumentCompareCreateDateParameterExists();
systemParametersExists &= checkIfDocumentTypesExceptionParameterExists();
systemParametersExists &= checkIfAnnotationForDisapprovalParameterExists();
return systemParametersExists;
}
/**
* This method checks for the system parameter for YEAR_END_AUTO_DISAPPROVE_DOCUMENTS_RUN_DATE
* @param outputErrorFile_ps output error file stream to write any error messages.
* @return true if YEAR_END_AUTO_DISAPPROVE_DOCUMENTS_RUN_DATE exists else false
*/
protected boolean checkIfRunDateParameterExists() {
boolean parameterExists = true;
// check to make sure the system parameter for run date check has already been setup...
if (!getParameterService().parameterExists(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_DOCUMENT_STEP_RUN_DATE)) {
LOG.warn("YEAR_END_AUTO_DISAPPROVE_DOCUMENT_RUN_DATE System parameter does not exist in the parameters list. The job can not continue without this parameter");
autoDisapproveErrorReportWriterService.writeFormattedMessageLine("YEAR_END_AUTO_DISAPPROVE_DOCUMENTS_RUN_DATE System parameter does not exist in the parameters list. The job can not continue without this parameter");
return false;
}
return parameterExists;
}
/**
* This method checks for the system parameter for YEAR_END_AUTO_DISAPPROVE_PARENT_DOCUMENT_TYPE
* @param outputErrorFile_ps output error file stream to write any error messages.
* @return true if YEAR_END_AUTO_DISAPPROVE_PARENT_DOCUMENT_TYPE exists else false
*/
protected boolean checkIfParentDocumentTypeParameterExists() {
boolean parameterExists = true;
// check to make sure the system parameter for Parent Document Type = FP has been setup...
if (!getParameterService().parameterExists(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_PARENT_DOCUMENT_TYPE)) {
LOG.warn("YEAR_END_AUTO_DISAPPROVE_PARENT_DOCUMENT_TYPE System parameter does not exist in the parameters list. The job can not continue without this parameter");
autoDisapproveErrorReportWriterService.writeFormattedMessageLine("YEAR_END_AUTO_DISAPPROVE_PARENT_DOCUMENT_TYPE System parameter does not exist in the parameters list. The job can not continue without this parameter");
return false;
}
return parameterExists;
}
/**
* This method checks for the system parameter for YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE
* @param outputErrorFile_ps output error file stream to write any error messages.
* @return true if YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE exists else false
*/
protected boolean checkIfDocumentCompareCreateDateParameterExists() {
boolean parameterExists = true;
// check to make sure the system parameter for create date to compare has been setup...
if (!getParameterService().parameterExists(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE)) {
LOG.warn("YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE System parameter does not exist in the parameters list. The job can not continue without this parameter");
autoDisapproveErrorReportWriterService.writeFormattedMessageLine("YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE System parameter does not exist in the parameters list. The job can not continue without this parameter");
return false;
}
return parameterExists;
}
/**
* This method checks for the system parameter for YEAR_END_AUTO_DISAPPROVE_DOCUMENT_TYPES
* @param outputErrorFile_ps output error file stream to write any error messages.
* @return true if YEAR_END_AUTO_DISAPPROVE_DOCUMENT_TYPES exists else false
*/
protected boolean checkIfDocumentTypesExceptionParameterExists() {
boolean parameterExists = true;
// check to make sure the system parameter for Document Types that are exceptions has been setup...
if (!getParameterService().parameterExists(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_DOCUMENT_TYPES)) {
LOG.warn("YEAR_END_AUTO_DISAPPROVE_DOCUMENT_TYPES System parameter does not exist in the parameters list. The job can not continue without this parameter");
autoDisapproveErrorReportWriterService.writeFormattedMessageLine("YEAR_END_AUTO_DISAPPROVE_DOCUMENT_TYPES System parameter does not exist in the parameters list. The job can not continue without this parameter");
return false;
}
return parameterExists;
}
/**
* This method checks for the system parameter for YEAR_END_AUTO_DISAPPROVE_ANNOTATION
* @param outputErrorFile_ps output error file stream to write any error messages.
* @return true if YEAR_END_AUTO_DISAPPROVE_ANNOTATION exists else false
*/
protected boolean checkIfAnnotationForDisapprovalParameterExists() {
boolean parameterExists = true;
// check to make sure the system parameter for annotation for notes has been setup...
if (!getParameterService().parameterExists(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_ANNOTATION)) {
LOG.warn("YEAR_END_AUTO_DISAPPROVE_ANNOTATION System parameter does not exist in the parameters list. The job can not continue without this parameter");
autoDisapproveErrorReportWriterService.writeFormattedMessageLine("YEAR_END_AUTO_DISAPPROVE_ANNOTATION System parameter does not exist in the parameters list. The job can not continue without this parameter");
return false;
}
return parameterExists;
}
/**
* This method will compare today's date to the system parameter for year end auto disapproval run date
* @return true if today's date equals to the system parameter run date
*/
protected boolean canAutoDisapproveJobRun() {
boolean autoDisapproveCanRun = true;
// IF trunc(SYSDATE - 14/24) = v_yec_cncl_doc_run_dt THEN...FIS CODE equivalent here...
String yearEndAutoDisapproveRunDate = getParameterService().getParameterValueAsString(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_DOCUMENT_STEP_RUN_DATE);
String today = getDateTimeService().toDateString(getDateTimeService().getCurrentDate());
if (!yearEndAutoDisapproveRunDate.equals(today)) {
LOG.warn("YEAR_END_AUTO_DISAPPROVE_DOCUMENTS_RUN_DATE: Automatic disapproval bypassed. The date on which the auto disapproval step should run: " + yearEndAutoDisapproveRunDate + " does not equal to today's date: " + today);
String message = ("YEAR_END_AUTO_DISAPPROVE_DOCUMENTS_RUN_DATE: Automatic disapproval bypassed. The date on which the auto disapproval step should run: ").concat(yearEndAutoDisapproveRunDate).concat(" does not equal to today's date: ").concat(today);
autoDisapproveErrorReportWriterService.writeFormattedMessageLine(message);
autoDisapproveCanRun = false;
}
return autoDisapproveCanRun;
}
/**
* This method will use DocumentSearchCriteria to search for the documents that are in enroute status and disapproves them
*
* @param principalId The principal id which is KFS-SYS System user to run the process under.
* @param annotation The annotation to be set as note in the note of the document.
* @param documentCompareDate The document create date to compare to
*/
protected boolean processAutoDisapproveDocuments(String principalId, String annotation, Date documentCompareDate) {
boolean success = true;
Collection<FinancialSystemDocumentHeader> documentList = this.getFinancialSystemDocumentService().findByWorkflowStatusCode(DocumentStatus.ENROUTE);
for (FinancialSystemDocumentHeader financialSystemDocumentHeader : documentList) {
if (checkIfDocumentEligibleForAutoDispproval(financialSystemDocumentHeader)) {
if (!exceptionsToAutoDisapproveProcess(financialSystemDocumentHeader, documentCompareDate)) {
try {
autoDisapprovalYearEndDocument(financialSystemDocumentHeader, annotation);
LOG.info("The document with header id: " + financialSystemDocumentHeader.getDocumentNumber() + " is automatically disapproved by this job.");
}
catch (Exception e) {
LOG.error("Exception encountered trying to auto disapprove the document " + e.getMessage());
String message = ("Exception encountered trying to auto disapprove the document: ").concat(financialSystemDocumentHeader.getDocumentNumber());
autoDisapproveErrorReportWriterService.writeFormattedMessageLine(message);
}
}
else {
LOG.info("Year End Auto Disapproval Exceptions: The document: " + financialSystemDocumentHeader.getDocumentNumber() + " is NOT AUTO DISAPPROVED.");
}
}
}
return success;
}
/**
* This method will check the document's document type against the parent document type as specified in the system parameter
* @param document
* @return true if document type of the document is a child of the parent document.
*/
protected boolean checkIfDocumentEligibleForAutoDispproval(DocumentHeader documentHeader) {
boolean documentEligible = false;
String yearEndAutoDisapproveParentDocumentType = getParameterService().getParameterValueAsString(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_PARENT_DOCUMENT_TYPE);
DocumentType parentDocumentType = getDocumentTypeService().getDocumentTypeByName(yearEndAutoDisapproveParentDocumentType);
String documentTypeName = documentHeader.getWorkflowDocument().getDocumentTypeName();
DocumentType childDocumentType = getDocumentTypeService().getDocumentTypeByName(documentTypeName);
documentEligible = childDocumentType.getParentId().equals(parentDocumentType.getId());
return documentEligible;
}
/**
* This method finds the date in the system parameters that will be used to compare the create date.
* It then adds 23 hours, 59 minutes and 59 seconds to the compare date.
* @return documentCompareDate returns YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE from the system parameter
*/
protected Date getDocumentCompareDateParameter() {
Date documentCompareDate = null;
String yearEndAutoDisapproveDocumentDate = getParameterService().getParameterValueAsString(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE);
if (ObjectUtils.isNull(yearEndAutoDisapproveDocumentDate)) {
LOG.warn("Exception: System Parameter YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE can not be determined.");
String message = ("Exception: The value for System Parameter YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE can not be determined. The auto disapproval job is stopped.");
autoDisapproveErrorReportWriterService.writeFormattedMessageLine(message);
throw new RuntimeException("Exception: AutoDisapprovalStep job stopped because System Parameter YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE is null");
}
try {
Date compareDate = getDateTimeService().convertToDate(yearEndAutoDisapproveDocumentDate);
Calendar calendar = Calendar.getInstance();
calendar.setTime(compareDate);
calendar.set(Calendar.HOUR, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
documentCompareDate = calendar.getTime();
}
catch (ParseException pe) {
LOG.warn("ParseException: System Parameter YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE can not be determined.");
String message = ("ParseException: The value for System Parameter YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE is invalid. The auto disapproval job is stopped.");
autoDisapproveErrorReportWriterService.writeFormattedMessageLine(message);
throw new RuntimeException("ParseException: AutoDisapprovalStep job stopped because System Parameter YEAR_END_AUTO_DISAPPROVE_DOCUMENT_CREATE_DATE is invalid");
}
return documentCompareDate;
}
/**
* This method finds the document for the given document header id
* @param documentHeaderId
* @return document The document in the workflow that matches the document header id.
*/
protected Document findDocumentForAutoDisapproval(String documentHeaderId) {
Document document = null;
try {
document = documentService.getByDocumentHeaderId(documentHeaderId);
}
catch (WorkflowException ex) {
LOG.error("Exception encountered on finding the document: " + documentHeaderId, ex );
} catch ( UnknownDocumentTypeException ex ) {
// don't blow up just because a document type is not installed (but don't return it either)
LOG.error("Exception encountered on finding the document: " + documentHeaderId, ex );
}
return document;
}
/**
* This method first checks the document's create date with system parameter date and then
* checks the document type name to the system parameter values and returns true if the type name exists
* @param document document to check for its document type, documentCompareDate the system parameter specified date
* to compare the current date to this date.
* @return true if document's create date is <= documentCompareDate and if document type is not in the
* system parameter document types that are set to disallow.
*/
protected boolean exceptionsToAutoDisapproveProcess(DocumentHeader documentHeader, Date documentCompareDate) {
boolean exceptionToDisapprove = true;
Date createDate = null;
String documentNumber = documentHeader.getDocumentNumber();
DateTime documentCreateDate = documentHeader.getWorkflowDocument().getDateCreated();
createDate = documentCreateDate.toDate();
Calendar calendar = Calendar.getInstance();
calendar.setTime(documentCompareDate);
String strCompareDate = calendar.getTime().toString();
calendar.setTime(createDate);
String strCreateDate = calendar.getTime().toString();
if (createDate.before(documentCompareDate) || createDate.equals(documentCompareDate)) {
String documentTypeName = documentHeader.getWorkflowDocument().getDocumentTypeName();
ParameterEvaluator evaluatorDocumentType = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(AutoDisapproveDocumentsStep.class, KFSParameterKeyConstants.YearEndAutoDisapprovalConstants.YEAR_END_AUTO_DISAPPROVE_DOCUMENT_TYPES, documentTypeName);
exceptionToDisapprove = !evaluatorDocumentType.evaluationSucceeds();
if (exceptionToDisapprove) {
LOG.info("Document Id: " + documentNumber + " - Exception to Auto Disapprove: Document's type: " + documentTypeName + " is in the System Parameter For Document Types Exception List.");
}
}
else {
LOG.info("Document Id: " + documentNumber + " - Exception to Auto Disapprove: Document's create date: " + strCreateDate + " is NOT less than or equal to System Parameter Compare Date: " + strCompareDate);
exceptionToDisapprove = true;
}
return exceptionToDisapprove;
}
/** autoDisapprovalYearEndDocument uses DocumentServiceImpl to mark as disapproved by calling
* DocumentServiceImpl's disapproveDocument method.
*
*@param document The document that needs to be auto disapproved in this process
*@param annotationForAutoDisapprovalDocument The annotationForAutoDisapprovalDocument that is set as annotations when canceling the edoc.
*
*/
protected void autoDisapprovalYearEndDocument(DocumentHeader documentHeader, String annotationForAutoDisapprovalDocument) throws Exception {
Person systemUser = getPersonService().getPersonByPrincipalName(KFSConstants.SYSTEM_USER);
Note approveNote = noteService.createNote(new Note(), documentHeader, systemUser.getPrincipalId());
approveNote.setNoteText(annotationForAutoDisapprovalDocument);
approveNote.setAuthorUniversalIdentifier(systemUser.getPrincipalId());
approveNote.setNotePostedTimestampToCurrent();
noteService.save(approveNote);
Document document = documentService.getByDocumentHeaderId(documentHeader.getDocumentNumber());
document.addNote(approveNote);
documentService.superUserDisapproveDocumentWithoutSaving(document, "Disapproval of Outstanding Documents - Year End Cancellation Process");
}
/**
* Sets the documentService attribute value.
*
* @param documentService The documentService to set.
*/
public void setDocumentService(DocumentService documentService) {
this.documentService = documentService;
}
/**
* Gets the documentService attribute.
*
* @return Returns the documentService.
*/
public DocumentService getDocumentService() {
return documentService;
}
/**
* Gets the parameterService attribute.
*
* @return Returns the parameterService.
*/
protected ParameterService getParameterService() {
return parameterService;
}
/**
* Sets the parameterService attribute value.
*
* @param parameterService The parameterService to set.
*/
public void setParameterService(ParameterService parameterService) {
this.parameterService = parameterService;
}
/**
* Gets the dateTimeService attribute.
*
* @return Returns the dateTimeService.
*/
protected DateTimeService getDateTimeService() {
return dateTimeService;
}
/**
* Sets the dateTimeService attribute value.
*
* @param dateTimeService The dateTimeService to set.
*/
public void setDateTimeService(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
/**
* Gets the NoteService, lazily initializing if necessary
* @return the NoteService
*/
protected synchronized NoteService getNoteService() {
if (this.noteService == null) {
this.noteService = SpringContext.getBean(NoteService.class);
}
return this.noteService;
}
/**
* Sets the noteService attribute value.
*
* @param noteService The noteService to set.
*/
public void setNoteService(NoteService noteService) {
this.noteService = noteService;
}
/**
* @return Returns the personService.
*/
protected PersonService getPersonService() {
if(personService==null) {
personService = SpringContext.getBean(PersonService.class);
}
return personService;
}
/**
* Gets the documentTypeService attribute.
*
* @return Returns the documentTypeService.
*/
protected DocumentTypeService getDocumentTypeService() {
if(documentTypeService==null) {
documentTypeService = SpringContext.getBean(DocumentTypeService.class);
}
return documentTypeService;
}
/**
* Gets the autoDisapproveErrorReportWriterService attribute.
* @return Returns the autoDisapproveErrorReportWriterService.
*/
protected ReportWriterService getAutoDisapproveErrorReportWriterService() {
return autoDisapproveErrorReportWriterService;
}
/**
* Sets the autoDisapproveErrorReportWriterService attribute value.
* @param autoDisapproveErrorReportWriterService The autoDisapproveErrorReportWriterService to set.
*/
public void setAutoDisapproveErrorReportWriterService(ReportWriterService autoDisapproveErrorReportWriterService) {
this.autoDisapproveErrorReportWriterService = autoDisapproveErrorReportWriterService;
}
/**
* Gets the financialSystemDocumentHeaderSerivce attribute
*
* @return
*/
public FinancialSystemDocumentService getFinancialSystemDocumentService() {
return financialSystemDocumentService;
}
/**
* Spring hook to inject financialSystemDocumentHeaderService
*
* @param financialSystemDocumentService
*/
public void setFinancialSystemDocumentService(FinancialSystemDocumentService financialSystemDocumentService) {
this.financialSystemDocumentService = financialSystemDocumentService;
}
}