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.fp.document.service.impl;
017    
018    import java.util.HashMap;
019    import java.util.List;
020    import java.util.Map;
021    import java.util.Set;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.kfs.fp.businessobject.DisbursementPayee;
025    import org.kuali.kfs.fp.businessobject.DisbursementVoucherPayeeDetail;
026    import org.kuali.kfs.fp.document.DisbursementVoucherConstants;
027    import org.kuali.kfs.fp.document.DisbursementVoucherDocument;
028    import org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService;
029    import org.kuali.kfs.sys.KFSPropertyConstants;
030    import org.kuali.kfs.sys.context.SpringContext;
031    import org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentAuthorizerBase;
032    import org.kuali.kfs.vnd.VendorConstants;
033    import org.kuali.kfs.vnd.businessobject.VendorDetail;
034    import org.kuali.kfs.vnd.businessobject.VendorType;
035    import org.kuali.kfs.vnd.document.service.VendorService;
036    import org.kuali.rice.kew.exception.WorkflowException;
037    import org.kuali.rice.kew.util.KEWConstants;
038    import org.kuali.rice.kim.bo.Person;
039    import org.kuali.rice.kim.service.PersonService;
040    import org.kuali.rice.kns.bo.AdHocRoutePerson;
041    import org.kuali.rice.kns.bo.Note;
042    import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
043    import org.kuali.rice.kns.service.BusinessObjectService;
044    import org.kuali.rice.kns.service.DataDictionaryService;
045    import org.kuali.rice.kns.service.DocumentService;
046    import org.kuali.rice.kns.service.ParameterService;
047    import org.kuali.rice.kns.util.ObjectUtils;
048    
049    /**
050     * implementing the service methods defined in DisbursementVoucherPayeeService
051     * 
052     * @see DisbursementVoucherPayeeService
053     */
054    public class DisbursementVoucherPayeeServiceImpl implements DisbursementVoucherPayeeService {
055        private org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DisbursementVoucherPayeeServiceImpl.class);
056    
057        private BusinessObjectService businessObjectService;
058        private DataDictionaryService dataDictionaryService;
059        private DocumentService documentService;
060        private ParameterService parameterService;
061        private VendorService vendorService;
062    
063        /**
064         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#getPayeeTypeDescription(java.lang.String)
065         */
066        public String getPayeeTypeDescription(String payeeTypeCode) {
067            String payeeTypeDescription = StringUtils.EMPTY;
068    
069            if (DisbursementVoucherConstants.DV_PAYEE_TYPE_EMPLOYEE.equals(payeeTypeCode)) {
070                payeeTypeDescription = parameterService.getParameterValue(DisbursementVoucherDocument.class, DisbursementVoucherConstants.NON_VENDOR_EMPLOYEE_PAYEE_TYPE_LABEL_PARM_NM);
071            }
072            else if (DisbursementVoucherConstants.DV_PAYEE_TYPE_VENDOR.equals(payeeTypeCode)) {
073                payeeTypeDescription = parameterService.getParameterValue(DisbursementVoucherDocument.class, DisbursementVoucherConstants.PO_AND_DV_PAYEE_TYPE_LABEL_PARM_NM);
074            }
075            else if (DisbursementVoucherConstants.DV_PAYEE_TYPE_REVOLVING_FUND_VENDOR.equals(payeeTypeCode)) {
076                payeeTypeDescription = this.getVendorTypeDescription(VendorConstants.VendorTypes.REVOLVING_FUND);
077            }
078            else if (DisbursementVoucherConstants.DV_PAYEE_TYPE_SUBJECT_PAYMENT_VENDOR.equals(payeeTypeCode)) {
079                payeeTypeDescription = this.getVendorTypeDescription(VendorConstants.VendorTypes.SUBJECT_PAYMENT);
080            }
081    
082            return payeeTypeDescription;
083        }
084    
085        /**
086         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#isEmployee(org.kuali.kfs.fp.businessobject.DisbursementVoucherPayeeDetail)
087         */
088        public boolean isEmployee(DisbursementVoucherPayeeDetail dvPayeeDetail) {
089            // If is vendor, then check vendor employee flag
090            if (this.isVendor(dvPayeeDetail)) {
091                VendorDetail vendor = vendorService.getByVendorNumber(dvPayeeDetail.getDisbVchrPayeeIdNumber());
092                return vendor == null ? false : vendorService.isVendorInstitutionEmployee(vendor.getVendorHeaderGeneratedIdentifier());
093            }
094    
095            String payeeTypeCode = dvPayeeDetail.getDisbursementVoucherPayeeTypeCode();
096            return DisbursementVoucherConstants.DV_PAYEE_TYPE_EMPLOYEE.equals(payeeTypeCode);
097        }
098    
099        /**
100         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#isEmployee(org.kuali.kfs.fp.businessobject.DisbursementPayee)
101         */
102        public boolean isEmployee(DisbursementPayee payee) {
103            // If is vendor, then check vendor employee flag
104            if (this.isVendor(payee)) {
105                VendorDetail vendor = vendorService.getByVendorNumber(payee.getPayeeIdNumber());
106                return vendor == null ? false : vendorService.isVendorInstitutionEmployee(vendor.getVendorHeaderGeneratedIdentifier());
107            }
108    
109            String payeeTypeCode = payee.getPayeeTypeCode();
110            return DisbursementVoucherConstants.DV_PAYEE_TYPE_EMPLOYEE.equals(payeeTypeCode);
111        }
112    
113        /**
114         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#isVendor(org.kuali.kfs.fp.businessobject.DisbursementVoucherPayeeDetail)
115         */
116        public boolean isVendor(DisbursementVoucherPayeeDetail dvPayeeDetail) {
117            String payeeTypeCode = dvPayeeDetail.getDisbursementVoucherPayeeTypeCode();
118            return DisbursementVoucherConstants.VENDOR_PAYEE_TYPE_CODES.contains(payeeTypeCode);
119        }
120    
121        /**
122         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#isVendor(org.kuali.kfs.fp.businessobject.DisbursementPayee)
123         */
124        public boolean isVendor(DisbursementPayee payee) {
125            String payeeTypeCode = payee.getPayeeTypeCode();
126            return DisbursementVoucherConstants.VENDOR_PAYEE_TYPE_CODES.contains(payeeTypeCode);
127        }
128    
129        /**
130         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#isPayeeIndividualVendor(org.kuali.kfs.fp.businessobject.DisbursementVoucherPayeeDetail)
131         */
132        public boolean isPayeeIndividualVendor(DisbursementVoucherPayeeDetail dvPayeeDetail) {
133            return this.isVendor(dvPayeeDetail) ? this.isPayeeIndividualVendor(dvPayeeDetail.getDisbVchrPayeeIdNumber()) : false;
134        }
135        
136        /**
137         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#isPayeeIndividualVendor(org.kuali.kfs.fp.businessobject.DisbursementPayee)
138         */
139        public boolean isPayeeIndividualVendor(DisbursementPayee payee) {
140            return this.isVendor(payee) ? this.isPayeeIndividualVendor(payee.getPayeeIdNumber()) : false;
141        }
142        
143        /**
144         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#getVendorOwnershipTypeCode(org.kuali.kfs.fp.businessobject.DisbursementPayee)
145         */
146        public String getVendorOwnershipTypeCode(DisbursementPayee payee) {
147            if(ObjectUtils.isNull(payee) || !this.isVendor(payee)) {
148                return null;
149            }
150            
151            VendorDetail vendor = vendorService.getByVendorNumber(payee.getPayeeIdNumber());
152            return vendor == null ? null : vendor.getVendorHeader().getVendorOwnershipCode();
153        }
154    
155        /**
156         * @see org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService#checkPayeeAddressForChanges(org.kuali.kfs.fp.document.DisbursementVoucherDocument)
157         */
158        public void checkPayeeAddressForChanges(DisbursementVoucherDocument dvDoc) {
159            Map<String, String> pks = new HashMap<String, String>();
160            pks.put("documentNumber", dvDoc.getDocumentNumber());
161    
162            DisbursementVoucherDocument savedDv = (DisbursementVoucherDocument) businessObjectService.findByPrimaryKey(DisbursementVoucherDocument.class, pks);
163            DisbursementVoucherPayeeDetail newPayeeDetail = dvDoc.getDvPayeeDetail();
164            DisbursementVoucherPayeeDetail oldPayeeDetail = savedDv.getDvPayeeDetail();
165    
166            if (ObjectUtils.isNotNull(oldPayeeDetail) && ObjectUtils.isNotNull(newPayeeDetail)) {
167                if (!oldPayeeDetail.hasSameAddress(newPayeeDetail)) {// Addresses don't match, so let's start the recording of
168                    // changes
169    
170                    // Put a note on the document to record the change to the address
171                    try {
172                        String noteText = buildPayeeChangedNoteText(newPayeeDetail, oldPayeeDetail);
173    
174                        int noteMaxSize = dataDictionaryService.getAttributeMaxLength("Note", "noteText");
175    
176                        // Break up the note into multiple pieces if the note is too large to fit in the database field.
177                        while (noteText.length() > noteMaxSize) {
178                            int fromIndex = 0;
179                            fromIndex = noteText.lastIndexOf(';', noteMaxSize);
180    
181                            String noteText1 = noteText.substring(0, fromIndex);
182                            Note note1 = documentService.createNoteFromDocument(dvDoc, noteText1);
183                            documentService.addNoteToDocument(dvDoc, note1);
184                            noteText = noteText.substring(fromIndex);
185                        }
186    
187                        Note note = documentService.createNoteFromDocument(dvDoc, noteText);
188                        documentService.addNoteToDocument(dvDoc, note);
189                    }
190                    catch (Exception e) {
191                        LOG.error("Exception while attempting to create or add note: " + e);
192                    }
193    
194                    // Send out FYIs to all previous approvers so they're aware of the changes to the address
195                    try {
196                        Set<Person> priorApprovers = dvDoc.getDocumentHeader().getWorkflowDocument().getAllPriorApprovers();
197                        String initiatorUserId = dvDoc.getDocumentHeader().getWorkflowDocument().getRouteHeader().getInitiatorPrincipalId();
198                        Person finSysUser = SpringContext.getBean(PersonService.class).getPerson(initiatorUserId);
199                        setupFYIs(dvDoc, priorApprovers, finSysUser.getPrincipalName());
200                    }
201                    catch (WorkflowException we) {
202                        LOG.error("Exception while attempting to retrieve all prior approvers from workflow: " + we);
203                    }
204                    catch (Exception unfe) {
205                        LOG.error("Exception while attempting to retrieve all prior approvers for a disbursement voucher: " + unfe);
206                    }
207                }
208            }
209        }
210    
211        /**
212         * Creates text for a note which records changes to the payee
213         * @param newPayeeDetail the changed payee detail
214         * @param oldPayeeDetail the original payee detail
215         * @return the string for a note
216         */
217        protected String buildPayeeChangedNoteText(DisbursementVoucherPayeeDetail newPayeeDetail, DisbursementVoucherPayeeDetail oldPayeeDetail) {
218            StringBuilder noteText = new StringBuilder();
219            String valueLabel = "";
220            try {
221                noteText.append("The following changes were made to the payee address: ");
222    
223                valueLabel = dataDictionaryService.getAttributeLabel(DisbursementVoucherPayeeDetail.class, KFSPropertyConstants.DISB_VCHR_PAYEE_LINE1_ADDR);
224                noteText.append(buildAddressValueDifferenceText(valueLabel, oldPayeeDetail.getDisbVchrPayeeLine1Addr(), newPayeeDetail.getDisbVchrPayeeLine1Addr()));
225    
226                valueLabel = dataDictionaryService.getAttributeLabel(DisbursementVoucherPayeeDetail.class, KFSPropertyConstants.DISB_VCHR_PAYEE_LINE2_ADDR);
227                noteText.append(buildAddressValueDifferenceText(valueLabel, oldPayeeDetail.getDisbVchrPayeeLine2Addr(), newPayeeDetail.getDisbVchrPayeeLine2Addr()));
228    
229                valueLabel = dataDictionaryService.getAttributeLabel(DisbursementVoucherPayeeDetail.class, KFSPropertyConstants.DISB_VCHR_PAYEE_CITY_NAME);
230                noteText.append(buildAddressValueDifferenceText(valueLabel, oldPayeeDetail.getDisbVchrPayeeCityName(), newPayeeDetail.getDisbVchrPayeeCityName()));
231    
232                valueLabel = dataDictionaryService.getAttributeLabel(DisbursementVoucherPayeeDetail.class, KFSPropertyConstants.DISB_VCHR_PAYEE_STATE_CODE);
233                noteText.append(buildAddressValueDifferenceText(valueLabel, oldPayeeDetail.getDisbVchrPayeeStateCode(), newPayeeDetail.getDisbVchrPayeeStateCode()));
234    
235                valueLabel = dataDictionaryService.getAttributeLabel(DisbursementVoucherPayeeDetail.class, KFSPropertyConstants.DISB_VCHR_PAYEE_ZIP_CODE);
236                noteText.append(buildAddressValueDifferenceText(valueLabel, oldPayeeDetail.getDisbVchrPayeeZipCode(), newPayeeDetail.getDisbVchrPayeeZipCode()));
237    
238                valueLabel = dataDictionaryService.getAttributeLabel(DisbursementVoucherPayeeDetail.class, KFSPropertyConstants.DISB_VCHR_PAYEE_COUNTRY_CODE);
239                noteText.append(buildAddressValueDifferenceText(valueLabel, oldPayeeDetail.getDisbVchrPayeeCountryCode(), newPayeeDetail.getDisbVchrPayeeCountryCode()));
240            }
241            catch (Exception ex) {
242                LOG.error("Error while attempting to build out note text for payee address change note: " + ex);
243            }
244    
245            return noteText.toString();
246        }
247    
248        /**
249         * This method...
250         * 
251         * @param valueName
252         * @param oldValue
253         * @param newValue
254         * @return
255         */
256        protected String buildAddressValueDifferenceText(String valueName, String oldValue, String newValue) {
257            // Nothing to log if values are still the same
258            if (StringUtils.equals(oldValue, newValue)) {
259                return "";
260            }
261    
262            StringBuilder text = new StringBuilder();
263    
264            text.append(valueName).append(" was changed from ");
265            text.append(oldValue == null ? "(no value entered)" : oldValue).append(" to ");
266            text.append(newValue).append("; ");
267    
268            return text.toString();
269        }
270    
271        /**
272         * Creates FYI requests to previous approvers
273         * 
274         * @param dvDoc the document where the payee address has changed
275         * @param priorApprovers the previous approvers
276         * @param initiatorUserId the id of the initiator
277         */
278        protected void setupFYIs(DisbursementVoucherDocument dvDoc, Set<Person> priorApprovers, String initiatorUserId) {
279            List<AdHocRoutePerson> adHocRoutePersons = dvDoc.getAdHocRoutePersons();
280            final FinancialSystemTransactionalDocumentAuthorizerBase documentAuthorizer = getDocumentAuthorizer(dvDoc);
281            
282            // Add FYI for each approver who has already approved the document
283            for (Person approver : priorApprovers) {
284                if (documentAuthorizer.canReceiveAdHoc(dvDoc, approver, KEWConstants.ACTION_REQUEST_FYI_REQ)) {
285                    String approverPersonUserId = approver.getPrincipalName();
286                    adHocRoutePersons.add(buildFyiRecipient(approverPersonUserId));
287                }
288            }
289    
290            // Add FYI for initiator
291            adHocRoutePersons.add(buildFyiRecipient(initiatorUserId));
292        }
293        
294        /**
295         * Constructs a document authorizer for this class
296         * @return the document authorizer for this class
297         */
298        protected FinancialSystemTransactionalDocumentAuthorizerBase getDocumentAuthorizer(DisbursementVoucherDocument dvDoc) {
299            final String docTypeName = dataDictionaryService.getDocumentTypeNameByClass(dvDoc.getClass());
300            Class<? extends DocumentAuthorizer> documentAuthorizerClass = dataDictionaryService.getDataDictionary().getDocumentEntry(docTypeName).getDocumentAuthorizerClass();
301            
302            FinancialSystemTransactionalDocumentAuthorizerBase documentAuthorizer = null;
303            try {
304                documentAuthorizer = (FinancialSystemTransactionalDocumentAuthorizerBase)documentAuthorizerClass.newInstance();
305            }
306            catch (InstantiationException ie) {
307                throw new RuntimeException("Could not construct document authorizer: "+documentAuthorizerClass.getName(), ie);
308            }
309            catch (IllegalAccessException iae) {
310                throw new RuntimeException("Could not construct document authorizer: "+documentAuthorizerClass.getName(), iae);
311            }
312            
313            return documentAuthorizer;
314        }
315    
316        /**
317         * This method...
318         * 
319         * @param userId
320         * @return
321         */
322        protected AdHocRoutePerson buildFyiRecipient(String userId) {
323            AdHocRoutePerson adHocRoutePerson = new AdHocRoutePerson();
324            adHocRoutePerson.setActionRequested(KEWConstants.ACTION_REQUEST_FYI_REQ);
325            adHocRoutePerson.setId(userId);
326            return adHocRoutePerson;
327        }
328    
329        // get the description of the vendor type with the given vendor type code
330        protected String getVendorTypeDescription(String vendorTypeCode) {
331            Map<String, String> primaryKeys = new HashMap<String, String>();
332            primaryKeys.put(KFSPropertyConstants.VENDOR_TYPE_CODE, vendorTypeCode);
333    
334            VendorType vendorType = (VendorType) businessObjectService.findByPrimaryKey(VendorType.class, primaryKeys);
335            return ObjectUtils.isNotNull(vendorType) ? vendorType.getVendorTypeDescription() : StringUtils.EMPTY;
336        }
337    
338        // determine whether the given payee id number is associated with an individual vendor
339        protected boolean isPayeeIndividualVendor(String payeeIdNumber) {
340            List<String> individualOwnerShipTypeCodes = parameterService.getParameterValues(DisbursementVoucherDocument.class, DisbursementVoucherConstants.INDIVIDUAL_OWNERSHIP_TYPES_PARM_NM);
341    
342            VendorDetail vendor = vendorService.getByVendorNumber(payeeIdNumber);
343            if (vendor != null && individualOwnerShipTypeCodes != null) {
344                return individualOwnerShipTypeCodes.contains(vendor.getVendorHeader().getVendorOwnershipCode());
345            }
346    
347            return false;
348        }
349    
350        /**
351         * Sets the businessObjectService attribute value.
352         * 
353         * @param businessObjectService The businessObjectService to set.
354         */
355        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
356            this.businessObjectService = businessObjectService;
357        }
358    
359        /**
360         * Sets the documentService attribute value.
361         * 
362         * @param documentService The documentService to set.
363         */
364        public void setDocumentService(DocumentService documentService) {
365            this.documentService = documentService;
366        }
367    
368        /**
369         * Sets the dataDictionaryService attribute value.
370         * 
371         * @param dataDictionaryService The dataDictionaryService to set.
372         */
373        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
374            this.dataDictionaryService = dataDictionaryService;
375        }
376    
377        /**
378         * Sets the parameterService attribute value.
379         * 
380         * @param parameterService The parameterService to set.
381         */
382        public void setParameterService(ParameterService parameterService) {
383            this.parameterService = parameterService;
384        }
385    
386        /**
387         * Sets the vendorService attribute value.
388         * @param vendorService The vendorService to set.
389         */
390        public void setVendorService(VendorService vendorService) {
391            this.vendorService = vendorService;
392        }
393    }