001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.endow.batch.service.impl;
017    
018    import java.sql.Date;
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import org.kuali.kfs.module.endow.EndowPropertyConstants;
026    import org.kuali.kfs.module.endow.batch.service.TicklerDeliveryService;
027    import org.kuali.kfs.module.endow.businessobject.GLInterfaceBatchStatisticsReportDetailTableRow;
028    import org.kuali.kfs.module.endow.businessobject.Tickler;
029    import org.kuali.kfs.module.endow.businessobject.TicklerDeliveryStatisticsReportDetailTableRow;
030    import org.kuali.kfs.module.endow.businessobject.TicklerRecipientGroup;
031    import org.kuali.kfs.module.endow.businessobject.TicklerRecipientPrincipal;
032    import org.kuali.kfs.module.endow.document.service.KEMService;
033    import org.kuali.kfs.sys.KFSConstants;
034    import org.kuali.kfs.sys.context.SpringContext;
035    import org.kuali.kfs.sys.service.ReportWriterService;
036    import org.kuali.kfs.sys.service.UniversityDateService;
037    import org.kuali.rice.kew.exception.WorkflowException;
038    import org.kuali.rice.kew.util.KEWConstants;
039    import org.kuali.rice.kns.bo.AdHocRoutePerson;
040    import org.kuali.rice.kns.bo.AdHocRouteRecipient;
041    import org.kuali.rice.kns.bo.AdHocRouteWorkgroup;
042    import org.kuali.rice.kns.document.MaintenanceDocument;
043    import org.kuali.rice.kns.maintenance.KualiMaintainableImpl;
044    import org.kuali.rice.kns.rule.event.SendAdHocRequestsEvent;
045    import org.kuali.rice.kns.service.BusinessObjectService;
046    import org.kuali.rice.kns.service.DocumentService;
047    import org.kuali.rice.kns.service.KualiRuleService;
048    import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
049    import org.kuali.rice.kns.util.KNSConstants;
050    import org.kuali.rice.kns.util.ObjectUtils;
051    import org.springframework.transaction.annotation.Transactional;
052    
053    @Transactional
054    public class TicklerDeliveryServiceImpl implements TicklerDeliveryService {
055    
056        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(TicklerDeliveryServiceImpl.class);
057        
058        private BusinessObjectService businessObjectService;
059        private KEMService kemService;
060        private UniversityDateService universityDateService;
061        private KualiRuleService kualiRuleService;
062        private DocumentService documentService;
063        private Date currentDate;
064        private ReportWriterService ticklerDeliveryStatisticsReportsWriterService;
065        
066        private TicklerDeliveryStatisticsReportDetailTableRow ticklerDeliveryStatisticsReportDetailTableRow;
067        
068        public TicklerDeliveryServiceImpl(){
069            //statistics report...
070            ticklerDeliveryStatisticsReportDetailTableRow = new TicklerDeliveryStatisticsReportDetailTableRow();
071        }
072        
073        public boolean generateTicklerNotices() {
074    
075            //set current date
076            currentDate = kemService.getCurrentDate();                
077            
078            LOG.info("Begin generateTicklerNotices() with notification date of " + currentDate);
079    
080            //get tickler documents
081            ArrayList<Tickler>ticklerBOs = new ArrayList<Tickler>(getTicklerBusinessObjects());
082            
083            //route tickler delivery notice
084            routeTicklerDeliveryNotice(ticklerBOs);
085            
086            writeStatisticsReport();
087            
088            LOG.info("End generateTicklerNotices() with notification date of " + currentDate);
089            
090            return false;
091        }
092    
093        /**
094         * Retrieves a list of tickler BOs where the next review date is
095         * equal to today, the term date is null or greater than today, and the record is active.
096         * 
097         * @param currentDate
098         * @return
099         */
100        protected List<Tickler> getTicklerBusinessObjects(){        
101            Map<String, Object> queryCriteria = new HashMap<String, Object>();
102            queryCriteria.put(EndowPropertyConstants.TICKLER_NEXT_DUE_DATE, currentDate);
103            queryCriteria.put(EndowPropertyConstants.TICKLER_ACTIVE_INDICATOR, KFSConstants.ParameterValues.YES);
104            
105            ArrayList<Tickler> ticklerBOs = null;
106            ticklerBOs = new ArrayList<Tickler>(businessObjectService.findMatching(Tickler.class, queryCriteria));
107            
108            //Go through and remove tickler docs where the term date has expired
109            if(ObjectUtils.isNotNull(ticklerBOs)){
110            
111                for(int i = ticklerBOs.size()-1; i > -1; i--){
112                    
113                    if(ObjectUtils.isNotNull(ticklerBOs.get(i).getTerminationDate()) && ticklerBOs.get(i).getTerminationDate().before(currentDate)){
114                        ticklerBOs.remove(i);
115                    }
116                }
117            }
118            
119            return ticklerBOs;
120        }
121            
122        /**
123         * Routes FYI tickler documents to Tickler persons and groups
124         * 
125         * @param ticklerDocs
126         * @return
127         */
128        protected boolean routeTicklerDeliveryNotice(List<Tickler> ticklerBOs){
129            boolean success = false;
130            boolean rulePassed = false;
131            MaintenanceDocument ticklerDocument = null;
132            
133            if(ObjectUtils.isNotNull(ticklerBOs)){
134            
135                for(Tickler ticklerBO : ticklerBOs){
136                    
137                    //create a maintenance document from the 
138                    ticklerDocument = createTicklerDocument(ticklerBO);
139                    
140                    //add principals and groups
141                    ticklerDocument.setAdHocRoutePersons(convertTicklerPrincipalToAdhocRoutePerson(ticklerBO.getRecipientPrincipals()));
142                    ticklerDocument.setAdHocRouteWorkgroups(convertTicklerGroupsToAdhocRouteGroup(ticklerBO.getRecipientGroups()));
143                    
144                    //check rules to ensure valid recipients
145                    rulePassed = kualiRuleService.applyRules(new SendAdHocRequestsEvent(ticklerDocument));
146                    
147                    if (rulePassed) {
148                        try{
149                            //rule passed to send adhoc requests
150                            documentService.routeDocument(ticklerDocument, "Tickler Notification - " + currentDate, combineAdHocRecipients(ticklerDocument));
151                            ticklerDeliveryStatisticsReportDetailTableRow.increaseTicklerDeliveryNotificationsCount();
152                            success = true;
153                        }catch(WorkflowException wfe){
154                            ticklerDeliveryStatisticsReportDetailTableRow.increaseNumberOfExceptionsCount();
155                            //just warn, but continue routing with other tickler BOs
156                            LOG.warn("Failed to route Tickler Delivery notices for Tickler Number " + ticklerBO.getNumber() + " with notification date of " + currentDate);
157                        }
158                    }else{
159                        ticklerDeliveryStatisticsReportDetailTableRow.increaseNumberOfExceptionsCount();
160                        LOG.warn("Invalid recipients for Tickler Delivery notices for Tickler Number " + ticklerBO.getNumber() + " with notification date of " + currentDate);
161                    }
162                    
163                    
164                }
165            }else{
166                //nothing to process
167                success = true;
168            }        
169            
170            return success;
171        }
172        
173        /**
174         * Creates a Tickler Maintenance Document based on a Tickler BO 
175         * 
176         * @param ticklerBo
177         * @return
178         */
179        protected MaintenanceDocument createTicklerDocument(Tickler ticklerBo){
180        
181            MaintenanceDocument document = null;
182            
183            try{
184                document = (MaintenanceDocument) documentService.getNewDocument(SpringContext.getBean(MaintenanceDocumentDictionaryService.class).getDocumentTypeName(ticklerBo.getClass()));            
185            }
186            catch (WorkflowException e) {
187                throw new RuntimeException(e);
188            }
189    
190            // add all the pieces
191            document.getDocumentHeader().setDocumentDescription("Tickler Notification - " + currentDate);
192            document.setOldMaintainableObject(new KualiMaintainableImpl(ticklerBo));
193            document.getOldMaintainableObject().setBoClass(ticklerBo.getClass());
194            document.setNewMaintainableObject(new KualiMaintainableImpl(ticklerBo));        
195            document.getNewMaintainableObject().setBoClass(ticklerBo.getClass());
196            document.getNewMaintainableObject().setMaintenanceAction(KNSConstants.MAINTENANCE_EDIT_ACTION);
197            document.getNewMaintainableObject().setDocumentNumber(document.getDocumentNumber());
198            
199            return document;
200        }
201        
202        /**
203         * Converts tickler principals into normal AdHocRoutePerson list
204         * 
205         * @param principals
206         * @return
207         */
208        protected List<AdHocRoutePerson> convertTicklerPrincipalToAdhocRoutePerson(List<TicklerRecipientPrincipal> principals){
209            List<AdHocRoutePerson> personList = new ArrayList<AdHocRoutePerson>();
210            AdHocRoutePerson person = null;
211            
212            if(ObjectUtils.isNotNull(principals)){
213                //for each principal, make an AdHocRoutePerson
214                for(TicklerRecipientPrincipal principal : principals){
215                    if(principal.isActive()){
216                        person = new AdHocRoutePerson();
217                        person.setId(principal.getContact().getPrincipalName());
218                        person.setActionRequested(KEWConstants.ACTION_REQUEST_FYI_REQ);                    
219                        personList.add(person);
220                    }
221                }
222            }
223            
224            return personList;
225        }
226        
227        /**
228         * Converts tickler groups into normal AdHocRouteWorkgroup list
229         * 
230         * @param groups
231         * @return
232         */
233        protected List<AdHocRouteWorkgroup> convertTicklerGroupsToAdhocRouteGroup(List<TicklerRecipientGroup> groups){
234            List<AdHocRouteWorkgroup> groupList = new ArrayList<AdHocRouteWorkgroup>();
235            AdHocRouteWorkgroup workgroup = null;
236            
237            if(ObjectUtils.isNotNull(groups)){
238                //for each group, make an AdHocWorkgroup
239                for(TicklerRecipientGroup group : groups){
240                    if(group.isActive()){
241                        workgroup = new AdHocRouteWorkgroup();
242                        workgroup.setId(group.getGroupId());
243                        workgroup.setRecipientName(group.getAssignedToGroup().getGroupName());
244                        workgroup.setRecipientNamespaceCode(group.getAssignedToGroup().getNamespaceCode());
245                        workgroup.setActionRequested(KEWConstants.ACTION_REQUEST_FYI_REQ);
246                        
247                        groupList.add(workgroup);
248                    }
249                }
250            }
251            
252            return groupList;
253        }
254        
255        /**
256         * Combines persons and workgroups from document into one list.
257         * 
258         * @param ticklerDocument
259         * @return
260         */
261        protected List<AdHocRouteRecipient> combineAdHocRecipients(MaintenanceDocument ticklerDocument) {
262            List<AdHocRouteRecipient> adHocRecipients = new ArrayList<AdHocRouteRecipient>();
263            adHocRecipients.addAll(ticklerDocument.getAdHocRoutePersons());
264            adHocRecipients.addAll(ticklerDocument.getAdHocRouteWorkgroups());
265            return adHocRecipients;
266        }
267    
268        protected void writeStatisticsReport() {
269            //now print the statistics report.....
270            long totalTicklerDeliveryNotifications = 0;
271            long totalNumberOfExceptions = 0;
272                    
273            //write the header line....
274            ticklerDeliveryStatisticsReportsWriterService.writeStatisticLine("Number of Tickler Notifications\t\tNumber of Exceptions");
275            ticklerDeliveryStatisticsReportsWriterService.writeStatisticLine("-------------------------------\t\t--------------------");
276                
277            ticklerDeliveryStatisticsReportsWriterService.writeStatisticLine("%31d\t\t%20d", ticklerDeliveryStatisticsReportDetailTableRow.getTicklerDeliveryNotifications(), ticklerDeliveryStatisticsReportDetailTableRow.getNumberOfExceptions());
278        }
279    
280        protected TicklerDeliveryStatisticsReportDetailTableRow getTicklerDeliveryStatisticsReportDetailTableRow() {
281            return ticklerDeliveryStatisticsReportDetailTableRow;
282        }
283    
284        public void setTicklerDeliveryStatisticsReportDetailTableRow(TicklerDeliveryStatisticsReportDetailTableRow ticklerDeliveryStatisticsReportDetailTableRow) {
285            this.ticklerDeliveryStatisticsReportDetailTableRow = ticklerDeliveryStatisticsReportDetailTableRow;
286        }
287    
288        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
289            this.businessObjectService = businessObjectService;
290        }
291    
292        public void setKemService(KEMService kemService) {
293            this.kemService = kemService;
294        }
295    
296        public void setUniversityDateService(UniversityDateService universityDateService) {
297            this.universityDateService = universityDateService;
298        }
299    
300        public void setKualiRuleService(KualiRuleService kualiRuleService) {
301            this.kualiRuleService = kualiRuleService;
302        }
303    
304        public void setDocumentService(DocumentService documentService) {
305            this.documentService = documentService;
306        }
307    
308        public void setTicklerDeliveryStatisticsReportsWriterService(ReportWriterService ticklerDeliveryStatisticsReportsWriterService) {
309            this.ticklerDeliveryStatisticsReportsWriterService = ticklerDeliveryStatisticsReportsWriterService;
310        }
311    
312    }