View Javadoc
1   /*
2               NoteService noteService = KRADServiceLocator.getNoteService();
3               notes = noteService.getByRemoteObjectId(this.getBusinessObject().getObjectId());
4    * Copyright 2007 The Kuali Foundation
5    * 
6    * Licensed under the Educational Community License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * 
10   * http://www.opensource.org/licenses/ecl2.php
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.kuali.ole.vnd.document;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.kuali.ole.sys.context.SpringContext;
27  import org.kuali.ole.sys.document.FinancialSystemMaintainable;
28  import org.kuali.ole.vnd.VendorConstants;
29  import org.kuali.ole.vnd.VendorKeyConstants;
30  import org.kuali.ole.vnd.VendorParameterConstants;
31  import org.kuali.ole.vnd.VendorPropertyConstants;
32  import org.kuali.ole.vnd.VendorUtils;
33  import org.kuali.ole.vnd.businessobject.VendorDetail;
34  import org.kuali.ole.vnd.businessobject.VendorHeader;
35  import org.kuali.ole.vnd.businessobject.VendorTaxChange;
36  import org.kuali.ole.vnd.document.service.VendorService;
37  import org.kuali.rice.core.api.datetime.DateTimeService;
38  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
39  import org.kuali.rice.kew.api.WorkflowDocument;
40  import org.kuali.rice.kim.api.identity.Person;
41  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
42  import org.kuali.rice.kns.document.MaintenanceDocument;
43  import org.kuali.rice.krad.bo.DocumentHeader;
44  import org.kuali.rice.krad.bo.Note;
45  import org.kuali.rice.krad.bo.PersistableBusinessObject;
46  import org.kuali.rice.krad.maintenance.MaintenanceLock;
47  import org.kuali.rice.krad.service.BusinessObjectService;
48  import org.kuali.rice.krad.service.KRADServiceLocator;
49  import org.kuali.rice.krad.service.NoteService;
50  import org.kuali.rice.krad.util.GlobalVariables;
51  import org.kuali.rice.krad.util.ObjectUtils;
52  
53  public class VendorMaintainableImpl extends FinancialSystemMaintainable {
54      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(VendorMaintainableImpl.class);
55  
56      /**
57       * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#setGenerateDefaultValues(boolean)
58       */
59  	@Override
60      public void setGenerateDefaultValues(String docTypeName) {
61          super.setGenerateDefaultValues(docTypeName);
62          
63          List<Note> notes = new ArrayList<Note>();
64  
65          if (getBusinessObject().getObjectId() != null) {
66              NoteService noteService = KRADServiceLocator.getNoteService();
67              notes = noteService.getByRemoteObjectId(this.getBusinessObject().getObjectId());
68                    
69              if (notes.isEmpty()) {
70                  notes.add(getNewBoNoteForAdding(VendorConstants.VendorCreateAndUpdateNotePrefixes.ADD));
71              }
72          }
73      }
74  
75      /**
76       * Overrides the kuali default documents title with a Vendor-specific document title style
77       * 
78       * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#getDocumentTitle(org.kuali.rice.kns.document.MaintenanceDocument)
79       */
80      @Override
81      public String getDocumentTitle(MaintenanceDocument document) {
82          String documentTitle = "";
83          // Check if we are choosing to override the Kuali default document title.
84          if (SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(VendorDetail.class, VendorParameterConstants.OVERRIDE_VENDOR_DOC_TITLE)) {
85              // We are overriding the standard with a Vendor-specific document title style.
86              if (document.isOldBusinessObjectInDocument()) {
87                  documentTitle = "Edit Vendor - ";
88              }
89              else {
90                  documentTitle = "New Vendor - ";
91              }
92  
93              try {
94                  Person initUser = KimApiServiceLocator.getPersonService().getPerson(document.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId());
95                  documentTitle += initUser.getCampusCode();
96              }
97              catch (Exception e) {
98                  throw new RuntimeException("Document Initiator not found " + e.getMessage());
99              }
100 
101             VendorDetail newBo = (VendorDetail) document.getNewMaintainableObject().getBusinessObject();
102 
103             if (StringUtils.isNotBlank(newBo.getVendorName())) {
104                 documentTitle += " '" + newBo.getVendorName() + "'";
105             } 
106             else {            
107                 if (StringUtils.isNotBlank(newBo.getVendorFirstName())) {
108                     documentTitle += " '" + newBo.getVendorFirstName() + " ";
109                     if (StringUtils.isBlank(newBo.getVendorLastName())) {
110                         documentTitle += "'";
111                     }
112                 }
113                 
114                 if (StringUtils.isNotBlank(newBo.getVendorLastName())) {
115                     if (StringUtils.isBlank(newBo.getVendorFirstName())) {
116                         documentTitle += " '";
117                     }
118                     documentTitle += newBo.getVendorLastName() + "'";
119                 }
120             }
121 
122             if (newBo.getVendorHeader().getVendorForeignIndicator()) {
123                 documentTitle += " (F)";
124             }
125 
126             if (!newBo.isVendorParentIndicator()) {
127                 documentTitle += " (D)";
128             }
129         }
130         else { // We are using the Kuali default document title.
131             documentTitle = super.getDocumentTitle(document);
132         }
133         return documentTitle;
134     }
135 
136     @Override
137     public void doRouteStatusChange(DocumentHeader header) {
138         super.doRouteStatusChange(header);
139         VendorDetail vendorDetail = (VendorDetail) getBusinessObject();
140         WorkflowDocument workflowDoc = header.getWorkflowDocument();
141 
142         // This code is only executed when the final approval occurs
143         if (workflowDoc.isProcessed()) {
144             // This id and versionNumber null check is needed here since those fields are always null for a fresh maintenance doc.
145             if (vendorDetail.isVendorParentIndicator() && vendorDetail.getVendorHeaderGeneratedIdentifier() != null) { 
146                 VendorDetail previousParent = SpringContext.getBean(VendorService.class).getParentVendor(vendorDetail.getVendorHeaderGeneratedIdentifier());
147                 // We'll only need to do the following if the previousParent is not the same as the current vendorDetail, because
148                 // the following lines are for vendor parent indicator changes.
149                 if (vendorDetail.getVendorDetailAssignedIdentifier() == null || 
150                         previousParent.getVendorHeaderGeneratedIdentifier().intValue() != vendorDetail.getVendorHeaderGeneratedIdentifier().intValue() || 
151                         previousParent.getVendorDetailAssignedIdentifier().intValue() != vendorDetail.getVendorDetailAssignedIdentifier().intValue()) {
152                     previousParent.setVendorParentIndicator(false);
153                     addNoteForParentIndicatorChange(vendorDetail, previousParent, header.getDocumentNumber());
154                     SpringContext.getBean(BusinessObjectService.class).save(previousParent);
155                 }
156             }
157 
158             // If this is a pre-existing parent vendor, and if the Tax Number or the Tax Type Code will change, log the change in the
159             // Tax Change table.
160             if (vendorDetail.isVendorParentIndicator()) {
161                 VendorDetail oldVendorDetail = SpringContext.getBean(VendorService.class).getVendorDetail(vendorDetail.getVendorHeaderGeneratedIdentifier(), vendorDetail.getVendorDetailAssignedIdentifier());
162                 if (ObjectUtils.isNotNull(oldVendorDetail)) {
163                     VendorHeader oldVendorHeader = oldVendorDetail.getVendorHeader();
164                     VendorHeader newVendorHeader = vendorDetail.getVendorHeader();
165 
166                     if (ObjectUtils.isNotNull(oldVendorHeader)) { // Does not apply if this is a new parent vendor.
167                         String oldVendorTaxNumber = oldVendorHeader.getVendorTaxNumber();
168                         String oldVendorTaxTypeCode = oldVendorHeader.getVendorTaxTypeCode();
169 
170                         String vendorTaxNumber = newVendorHeader.getVendorTaxNumber();
171                         String vendorTaxTypeCode = newVendorHeader.getVendorTaxTypeCode();
172 
173                         if ((!StringUtils.equals(vendorTaxNumber, oldVendorTaxNumber)) || (!StringUtils.equals(vendorTaxTypeCode, oldVendorTaxTypeCode))) {
174                             VendorTaxChange taxChange = new VendorTaxChange(vendorDetail.getVendorHeaderGeneratedIdentifier(), SpringContext.getBean(DateTimeService.class).getCurrentTimestamp(), oldVendorTaxNumber, oldVendorTaxTypeCode, GlobalVariables.getUserSession().getPerson().getPrincipalId());
175                             SpringContext.getBean(BusinessObjectService.class).save(taxChange);
176                         }
177                     }
178                 }
179             }
180 
181         }//endif isProcessed()
182     }
183     
184     /**
185      * Add a note to the previous parent vendor to denote that parent vendor indicator change had occurred.
186      * 
187      * @param newVendorDetail The current vendor
188      * @param oldVendorDetail The parent vendor of the current vendor prior to this change.
189      * @param getDocumentNumber() The document number of the document where we're attempting the parent vendor indicator change.
190      */
191     private void addNoteForParentIndicatorChange(VendorDetail newVendorDetail, VendorDetail oldVendorDetail, String docNumber) {
192         String noteText = VendorUtils.buildMessageText(VendorKeyConstants.MESSAGE_VENDOR_PARENT_TO_DIVISION, docNumber, newVendorDetail.getVendorName() + " (" + newVendorDetail.getVendorNumber() + ")");   
193         Note newBONote = new Note();
194         newBONote.setNoteText(noteText);
195         try {
196             NoteService noteService = SpringContext.getBean(NoteService.class);
197             newBONote = noteService.createNote(newBONote, oldVendorDetail, GlobalVariables.getUserSession().getPrincipalId());
198             newBONote.setNotePostedTimestampToCurrent();
199             
200             noteService.save(newBONote);
201         }
202         catch (Exception e) {
203             throw new RuntimeException("Caught Exception While Trying To Add Note to Vendor", e);
204         }
205         
206         NoteService noteService = KRADServiceLocator.getNoteService();
207         List<Note> notes = noteService.getByRemoteObjectId(oldVendorDetail.getObjectId());
208         notes.add(newBONote);
209     }
210     
211     /**
212      * Refreshes the vendorDetail. Currently we need this mainly for refreshing the soldToVendor object after returning from the
213      * lookup for a sold to vendor.
214      * 
215      * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#refresh(java.lang.String, java.util.Map,
216      *      org.kuali.rice.kns.document.MaintenanceDocument)
217      */
218     @Override
219     public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) {
220         PersistableBusinessObject oldBo = (PersistableBusinessObject) document.getOldMaintainableObject().getBusinessObject();
221         if (ObjectUtils.isNotNull(oldBo)) {
222             oldBo.refreshNonUpdateableReferences();
223         }
224         VendorDetail newBo = (VendorDetail) document.getNewMaintainableObject().getBusinessObject();
225         // Here we have to temporarily save vendorHeader into a temp object, then put back
226         // the vendorHeader into the newBo after the refresh, so that we don't lose the
227         // values
228         VendorHeader tempHeader = newBo.getVendorHeader();
229         newBo.refreshNonUpdateableReferences();
230         newBo.setVendorHeader(tempHeader);
231         super.refresh(refreshCaller, fieldValues, document);
232     }
233 
234     /**
235      * Temporarily saves vendorHeader into a temp object, then put back the vendorHeader into the VendorDetail after the refresh, so
236      * that we don't lose the values
237      */
238     public void refreshBusinessObject() {
239         VendorDetail vd = (VendorDetail) getBusinessObject();
240         // Here we have to temporarily save vendorHeader into a temp object, then put back
241         // the vendorHeader into the VendorDetail after the refresh, so that we don't lose the
242         // values
243         VendorHeader tempHeader = vd.getVendorHeader();
244         vd.refreshNonUpdateableReferences();
245         vd.setVendorHeader(tempHeader);
246     }
247 
248 
249     /**
250      * Checks whether the vendor has already had a vendor detail assigned id. If not, it will call the private method to set the
251      * detail assigned id. The method will also call the vendorService to determine whether it should save the vendor header (i.e.
252      * if this is a parent) and will save the vendor header accordingly. This is because we are not going to save vendor header
253      * automatically along with the saving of vendor detail, so if the vendor is a parent, we have to save the vendor header
254      * separately. Restriction-related information will be changed based on whether the Vendor Restricted Indicator was changed. If
255      * the Tax Number or Tax Type code have changed, the fact will be recorded with a new record in the Tax Change table. Finally
256      * the method will call the saveBusinessObject( ) of the super class to save the vendor detail.
257      * 
258      * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#saveBusinessObject()
259      */
260     @Override
261     public void saveBusinessObject() {
262         VendorDetail vendorDetail = (VendorDetail) super.getBusinessObject();
263         VendorHeader vendorHeader = vendorDetail.getVendorHeader();
264 
265         // Update miscellaneous information and save the Vendor Header if this is a parent vendor.
266         setVendorName(vendorDetail);
267         vendorHeader.setVendorHeaderGeneratedIdentifier(vendorDetail.getVendorHeaderGeneratedIdentifier());
268         if (ObjectUtils.isNull(vendorDetail.getVendorDetailAssignedIdentifier())) {
269             setDetailAssignedId(vendorDetail);
270         }
271         if (vendorDetail.isVendorParentIndicator()) {
272             SpringContext.getBean(VendorService.class).saveVendorHeader(vendorDetail);
273         }
274         super.saveBusinessObject();
275     }
276 
277     /**
278      * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterEdit()
279      */
280     @Override
281     public void processAfterEdit( MaintenanceDocument document, Map<String,String[]> parameters ) {
282 
283         List<Note> notes = new ArrayList<Note>();
284         if (document.getOldMaintainableObject().getBusinessObject().getObjectId() != null) {
285             NoteService noteService = KRADServiceLocator.getNoteService();
286             notes = noteService.getByRemoteObjectId(this.getBusinessObject().getObjectId());
287         }
288         
289         setVendorCreateAndUpdateNote(notes, VendorConstants.VendorCreateAndUpdateNotePrefixes.CHANGE);
290         document.setNotes(notes);
291         
292         super.processAfterEdit(document, parameters);
293     }
294 
295     /**
296      * Checks whether the previous note was an "Add" with the same document number as this one
297      * 
298      * @param notes List of exisiting notes.
299      * @param prefix String to determine if it is a note "Add" or a note "Change"
300      */
301     private void setVendorCreateAndUpdateNote(List<Note> notes, String prefix) {
302         boolean shouldAddNote = true;
303         
304         if (prefix.equals(VendorConstants.VendorCreateAndUpdateNotePrefixes.CHANGE)) {
305             // Check whether the previous note was an "Add" with the same document number as this one
306             if (!notes.isEmpty()) {
307                 Note previousNote = notes.get(notes.size() - 1 );
308                 if (previousNote.getNoteText().contains(getDocumentNumber())) {
309                     shouldAddNote = false;
310                 }
311             }
312         }
313         if (shouldAddNote) {
314             notes.add(getNewBoNoteForAdding(prefix));
315         }
316     }
317 
318     /**
319      * creates a new bo note and sets the timestamp.
320      * 
321      * @return a newly created note
322      */
323     protected Note getNewBoNoteForAdding(String prefix) {
324         Note newBoNote = new Note();
325         newBoNote.setNoteText(prefix + " vendor document ID " + getDocumentNumber());
326         newBoNote.setNotePostedTimestampToCurrent();
327        
328             try {
329             newBoNote = SpringContext.getBean(NoteService.class).createNote(newBoNote, this.getBusinessObject(), GlobalVariables.getUserSession().getPrincipalId());
330             }
331             catch (Exception e) {
332                 throw new RuntimeException("Caught Exception While Trying To Add Note to Vendor", e);
333             }
334         
335         return newBoNote;
336     }
337 
338     /**
339      * Concatenates the vendorLastName and a delimiter and the vendorFirstName fields into vendorName field of the vendorDetail
340      * object.
341      * 
342      * @param vendorDetail VendorDetail The vendor whose name field we are trying to assign
343      */
344     private void setVendorName(VendorDetail vendorDetail) {
345         if (vendorDetail.isVendorFirstLastNameIndicator()) {
346             vendorDetail.setVendorName(vendorDetail.getVendorLastName() + VendorConstants.NAME_DELIM + vendorDetail.getVendorFirstName());
347         }
348     }
349 
350     /**
351      * If the vendorFirstLastNameIndicator is true, this method will set the vendor first name and vendor last name fields from the
352      * vendorName field, then set the vendorName field to null. Then it sets the businessObject of this maintainable to the
353      * VendorDetail object that contains our modification to the name fields.
354      * 
355      * @see org.kuali.rice.kns.maintenance.Maintainable#saveBusinessObject()
356      */
357     @Override
358     public void setBusinessObject(PersistableBusinessObject bo) {
359         VendorDetail originalBo = (VendorDetail) bo;
360         
361         String vendorName = originalBo.getVendorName();
362         if (originalBo.isVendorFirstLastNameIndicator() && ObjectUtils.isNotNull(vendorName)) {
363             int start = vendorName.indexOf(VendorConstants.NAME_DELIM);
364             if (start >= 0) {
365                 String lastName = vendorName.substring(0, start);
366                 String firstName = new String();
367                 if (start + VendorConstants.NAME_DELIM.length() <= vendorName.length()) {
368                     firstName = vendorName.substring(start + VendorConstants.NAME_DELIM.length(), vendorName.length());
369                 }
370 
371                 originalBo.setVendorFirstName((ObjectUtils.isNotNull(firstName) ? firstName.trim() : firstName));
372                 originalBo.setVendorLastName((ObjectUtils.isNotNull(lastName) ? lastName.trim() : lastName));
373                 originalBo.setVendorName(null);
374             }
375         }
376         
377         super.setBusinessObject(originalBo);
378     }
379 
380     /**
381      * Sets a valid detail assigned id to a vendor if the vendor has not had a detail assigned id yet. If this is a new parent whose
382      * header id is also null, this method will assign 0 as the detail assigned id. If this is a new division vendor, it will look
383      * for the count of vendor details in the database whose vendor header id match with the vendor header id of this new division,
384      * then look for the count of vendor details in the database, in a while loop, to find if a vendor detail with the same header
385      * id and detail id as the count has existed. If a vendor with such criteria exists, this method will increment the count
386      * by 1 and look up in the database again. If it does not exist, assign the count as the vendor detail id and change the
387      * boolean flag to stop the loop, because we have already found the valid detail assigned id that we were looking for
388      * 
389      * @param vendorDetail VendorDetail The vendor whose detail assigned id we're trying to assign.
390      */
391     private void setDetailAssignedId(VendorDetail vendorDetail) {
392         // If this is a new parent, let's set the detail id to 0.
393         if (ObjectUtils.isNull(vendorDetail.getVendorHeaderGeneratedIdentifier())) {
394             vendorDetail.setVendorDetailAssignedIdentifier(new Integer(0));
395         }
396         else {
397             // Try to get the count of all the vendor whose header id is the same as this header id.
398             Map criterias = new HashMap();
399             criterias.put(VendorPropertyConstants.VENDOR_HEADER_GENERATED_ID, vendorDetail.getVendorHeaderGeneratedIdentifier());
400             BusinessObjectService boService = SpringContext.getBean(BusinessObjectService.class);
401             int count = boService.countMatching(VendorDetail.class, criterias);
402             boolean validId = false;
403             while (!validId) {
404                 criterias.put(VendorPropertyConstants.VENDOR_DETAIL_ASSIGNED_ID, count);
405                 int result = boService.countMatching(VendorDetail.class, criterias);
406                 if (result > 0) {
407                     // increment the detail id by 1
408                     count++;
409                 }
410                 else {
411                     // count is a validId, so we'll use count as our vendor detail assigned id
412                     validId = true;
413                     vendorDetail.setVendorDetailAssignedIdentifier(new Integer(count));
414                 }
415             }
416         }
417     }
418 
419     /**
420      * Returns the locking representation of the vendor. If the vendor detail id is not null, call the super class
421      * implementation of generateMaintenanceLocks which will set the locking key to be the header and detail ids. However, if the
422      * detail id is null, that means this is a new vendor (parent or division) and we should ignore locking.
423      * 
424      * @see org.kuali.rice.kns.maintenance.Maintainable#generateMaintenanceLocks()
425      */
426     @Override
427     public List<MaintenanceLock> generateMaintenanceLocks() {
428         if (ObjectUtils.isNotNull(((VendorDetail) getBusinessObject()).getVendorDetailAssignedIdentifier())) {
429             return super.generateMaintenanceLocks();
430         }
431         else {
432             return new ArrayList();
433         }
434     }
435 
436     /**
437      * Create a new division vendor if the user clicks on the "Create a new division" link. By default, the vendorParentIndicator is
438      * set to true in the constructor of VendorDetail, but if we're creating a new division, it's not a parent, so we need to set
439      * the vendorParentIndicator to false in this case.
440      * 
441      * @see org.kuali.rice.kns.maintenance.Maintainable#setupNewFromExisting()
442      */
443     @Override
444     public void setupNewFromExisting( MaintenanceDocument document, Map<String,String[]> parameters ) {
445         super.setupNewFromExisting(document, parameters);
446         
447         ((VendorDetail) super.getBusinessObject()).setVendorParentIndicator(false);
448         ((VendorDetail) super.getBusinessObject()).setActiveIndicator(true);
449 
450         List<Note> notes = new ArrayList<Note>();
451 
452         if (getBusinessObject().getObjectId() != null) {
453             NoteService noteService = KRADServiceLocator.getNoteService();
454             notes = noteService.getByRemoteObjectId(this.getBusinessObject().getObjectId());
455         }
456         
457         setVendorCreateAndUpdateNote(notes, VendorConstants.VendorCreateAndUpdateNotePrefixes.ADD);
458 
459         document.setNotes(notes);
460     }
461 
462     /**
463      * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#isRelationshipRefreshable(java.lang.Class, java.lang.String)
464      */
465     @Override
466     protected boolean isRelationshipRefreshable(Class boClass, String relationshipName) {
467         if (VendorDetail.class.isAssignableFrom(boClass) && VendorConstants.VENDOR_HEADER_ATTR.equals(relationshipName)) {
468             return false;
469         }
470         return super.isRelationshipRefreshable(boClass, relationshipName);
471     }
472 
473     /**
474      * @see org.kuali.ole.sys.document.FinancialSystemMaintainable#answerSplitNodeQuestion(java.lang.String)
475      */
476     @Override
477     protected boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
478         if (nodeName.equals("RequiresApproval")) return SpringContext.getBean(VendorService.class).shouldVendorRouteForApproval(getDocumentNumber());
479         return super.answerSplitNodeQuestion(nodeName);
480     }
481 }
482