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 }