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.purap.document.web.struts;
017    
018    import java.math.BigDecimal;
019    import java.util.Arrays;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import javax.servlet.http.HttpServletRequest;
025    
026    import org.apache.commons.lang.StringUtils;
027    import org.kuali.kfs.integration.purap.CapitalAssetLocation;
028    import org.kuali.kfs.module.purap.PurapAuthorizationConstants;
029    import org.kuali.kfs.module.purap.PurapConstants;
030    import org.kuali.kfs.module.purap.PurapWorkflowConstants;
031    import org.kuali.kfs.module.purap.PurapConstants.PaymentRequestStatuses;
032    import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderStatuses;
033    import org.kuali.kfs.module.purap.businessobject.PaymentRequestView;
034    import org.kuali.kfs.module.purap.businessobject.PurApItem;
035    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderAccount;
036    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderCapitalAssetLocation;
037    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
038    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItemCapitalAsset;
039    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderVendorQuote;
040    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderVendorStipulation;
041    import org.kuali.kfs.module.purap.businessobject.RequisitionCapitalAssetLocation;
042    import org.kuali.kfs.module.purap.businessobject.SensitiveData;
043    import org.kuali.kfs.module.purap.businessobject.SensitiveDataAssignment;
044    import org.kuali.kfs.module.purap.document.LineItemReceivingDocument;
045    import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
046    import org.kuali.kfs.module.purap.document.PurchaseOrderAmendmentDocument;
047    import org.kuali.kfs.module.purap.document.PurchaseOrderCloseDocument;
048    import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
049    import org.kuali.kfs.module.purap.document.PurchaseOrderPaymentHoldDocument;
050    import org.kuali.kfs.module.purap.document.PurchaseOrderRemoveHoldDocument;
051    import org.kuali.kfs.module.purap.document.PurchaseOrderReopenDocument;
052    import org.kuali.kfs.module.purap.document.PurchaseOrderRetransmitDocument;
053    import org.kuali.kfs.module.purap.document.PurchaseOrderSplitDocument;
054    import org.kuali.kfs.module.purap.document.PurchaseOrderVoidDocument;
055    import org.kuali.kfs.module.purap.document.service.PaymentRequestService;
056    import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
057    import org.kuali.kfs.module.purap.document.service.ReceivingService;
058    import org.kuali.kfs.module.purap.util.PurApItemUtils;
059    import org.kuali.kfs.sys.KFSConstants;
060    import org.kuali.kfs.sys.context.SpringContext;
061    import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
062    import org.kuali.rice.kns.service.DataDictionaryService;
063    import org.kuali.rice.kns.service.DateTimeService;
064    import org.kuali.rice.kns.service.DocumentHelperService;
065    import org.kuali.rice.kns.util.GlobalVariables;
066    import org.kuali.rice.kns.util.KNSConstants;
067    import org.kuali.rice.kns.util.ObjectUtils;
068    import org.kuali.rice.kns.web.ui.ExtraButton;
069    import org.kuali.rice.kns.web.ui.HeaderField;
070    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
071    
072    /**
073     * Struts Action Form for Purchase Order document.
074     */
075    public class PurchaseOrderForm extends PurchasingFormBase {
076    
077        protected PurchaseOrderVendorStipulation newPurchaseOrderVendorStipulationLine;
078        protected PurchaseOrderVendorQuote newPurchaseOrderVendorQuote;
079        protected Long awardedVendorNumber;
080        
081        // Retransmit.
082        protected String[] retransmitItemsSelected = {};
083        protected String retransmitTransmissionMethod;
084        protected String retransmitFaxNumber;
085        protected String retransmitHeader;
086    
087        // Need this for amendment for accounting line only
088        protected Map accountingLineEditingMode;
089        
090        protected String splitNoteText;
091    
092        // Assign Sensitive Data related fields
093        protected String sensitiveDataAssignmentReason = null; // reason for current assignment of sensitive data to the PO
094        protected SensitiveDataAssignment lastSensitiveDataAssignment = null; // last sensitive data assignment info for the PO
095        protected SensitiveData newSensitiveDataLine = null; // new sensitive data entry to be added to the PO
096        protected List<SensitiveData> sensitiveDatasAssigned = null;  // sensitive data entries currently assigned to the PO
097    
098        /**
099         * Constructs a PurchaseOrderForm instance and sets up the appropriately casted document.
100         */
101        public PurchaseOrderForm() {
102            super();
103    
104            setNewPurchaseOrderVendorStipulationLine(new PurchaseOrderVendorStipulation());
105            setNewPurchaseOrderVendorQuote(new PurchaseOrderVendorQuote());
106            this.accountingLineEditingMode = new HashMap();
107        }
108    
109        @Override
110        protected String getDefaultDocumentTypeName() {
111            return "PO";
112        }
113        
114        public Map getAccountingLineEditingMode() {
115            return accountingLineEditingMode;
116        }
117    
118        public void setAccountingLineEditingMode(Map accountingLineEditingMode) {
119            this.accountingLineEditingMode = accountingLineEditingMode;
120        }
121    
122        public Long getAwardedVendorNumber() {
123            return awardedVendorNumber;
124        }
125    
126        public void setAwardedVendorNumber(Long awardedVendorNumber) {
127            this.awardedVendorNumber = awardedVendorNumber;
128        }
129    
130        public PurchaseOrderVendorStipulation getNewPurchaseOrderVendorStipulationLine() {
131            return newPurchaseOrderVendorStipulationLine;
132        }
133    
134        public void setNewPurchaseOrderVendorStipulationLine(PurchaseOrderVendorStipulation newPurchaseOrderVendorStipulationLine) {
135            this.newPurchaseOrderVendorStipulationLine = newPurchaseOrderVendorStipulationLine;
136        }
137    
138        public PurchaseOrderVendorQuote getNewPurchaseOrderVendorQuote() {
139            return newPurchaseOrderVendorQuote;
140        }
141    
142        public void setNewPurchaseOrderVendorQuote(PurchaseOrderVendorQuote newPurchaseOrderVendorQuote) {
143            this.newPurchaseOrderVendorQuote = newPurchaseOrderVendorQuote;
144        }
145    
146        public String[] getRetransmitItemsSelected() {
147            return retransmitItemsSelected;
148        }
149    
150        public void setRetransmitItemsSelected(String[] retransmitItemsSelected) {
151            this.retransmitItemsSelected = retransmitItemsSelected;
152        }
153    
154        public PurchaseOrderDocument getPurchaseOrderDocument() {
155            return (PurchaseOrderDocument) getDocument();
156        }
157    
158        public void setPurchaseOrderDocument(PurchaseOrderDocument purchaseOrderDocument) {
159            setDocument(purchaseOrderDocument);
160        }
161        
162        public String getSplitNoteText() {
163            return splitNoteText;
164        }
165    
166        public void setSplitNoteText(String splitNoteText) {
167            this.splitNoteText = splitNoteText;
168        }       
169        
170        public String getSensitiveDataAssignmentReason() {
171            return sensitiveDataAssignmentReason;
172        }
173    
174        public void setSensitiveDataAssignmentReason(String sensitiveDataAssignmentReason) {
175            this.sensitiveDataAssignmentReason = sensitiveDataAssignmentReason;
176        }
177    
178        public SensitiveDataAssignment getLastSensitiveDataAssignment() {
179            return lastSensitiveDataAssignment;
180        }
181    
182        public void setLastSensitiveDataAssignment(SensitiveDataAssignment lastSensitiveDataAssignment) {
183            this.lastSensitiveDataAssignment = lastSensitiveDataAssignment;
184        }
185    
186        public SensitiveData getNewSensitiveDataLine() {
187            return newSensitiveDataLine;
188        }
189    
190        public void setNewSensitiveDataLine(SensitiveData newSensitiveDataLine) {
191            this.newSensitiveDataLine = newSensitiveDataLine;
192        }
193    
194        public List<SensitiveData> getSensitiveDatasAssigned() {
195            return sensitiveDatasAssigned;
196        }
197    
198        public void setSensitiveDatasAssigned(List<SensitiveData> poSensitiveData) {
199            this.sensitiveDatasAssigned = poSensitiveData;
200        }
201        
202        @Override
203        public Class getCapitalAssetLocationClass() {
204            return PurchaseOrderCapitalAssetLocation.class;
205        }
206    
207        @Override
208        public Class getItemCapitalAssetClass() {
209            return PurchaseOrderItemCapitalAsset.class;
210        }
211    
212        @Override
213        public CapitalAssetLocation setupNewPurchasingCapitalAssetLocationLine() {
214            CapitalAssetLocation location = new RequisitionCapitalAssetLocation();
215            return location;
216        }    
217        
218        /**
219         * @see org.kuali.kfs.module.purap.document.web.struts.PurchasingFormBase#setupNewPurchasingItemLine()
220         */
221        @Override
222        public PurApItem setupNewPurchasingItemLine() {
223            return new PurchaseOrderItem();
224        }
225    
226        /**
227         * @see org.kuali.kfs.module.purap.document.web.struts.PurchasingFormBase#setupNewPurchasingAccountingLine()
228         */
229        @Override
230        public PurchaseOrderAccount setupNewPurchasingAccountingLine() {
231            return new PurchaseOrderAccount();
232        }
233    
234        /**
235         * @see org.kuali.kfs.module.purap.document.web.struts.PurchasingFormBase#setupNewAccountDistributionAccountingLine()
236         */
237        @Override
238        public PurchaseOrderAccount setupNewAccountDistributionAccountingLine() {
239            PurchaseOrderAccount account = setupNewPurchasingAccountingLine();
240            account.setAccountLinePercent(new BigDecimal(100));
241            return account;
242        }
243    
244        public boolean isReadOnlyReceivingRequired() {
245            
246            PurchaseOrderDocument poDoc = getPurchaseOrderDocument();
247            
248            if (poDoc instanceof PurchaseOrderAmendmentDocument){
249                if (!poDoc.isReceivingDocumentRequiredIndicator()){
250                    return SpringContext.getBean(PaymentRequestService.class).hasActivePaymentRequestsForPurchaseOrder(poDoc.getPurapDocumentIdentifier());
251                }else{
252                    return true;
253                }
254            }
255    
256            return false;
257        }
258    
259        /**
260         * Returns the new Purchase Order Vendor Stipulation Line and resets it.
261         * 
262         * @return the new Purchase Order Vendor Stipulation Line.
263         */
264        public PurchaseOrderVendorStipulation getAndResetNewPurchaseOrderVendorStipulationLine() {
265            PurchaseOrderVendorStipulation aPurchaseOrderVendorStipulationLine = getNewPurchaseOrderVendorStipulationLine();
266            setNewPurchaseOrderVendorStipulationLine(new PurchaseOrderVendorStipulation());
267    
268            aPurchaseOrderVendorStipulationLine.setDocumentNumber(getPurchaseOrderDocument().getDocumentNumber());
269            aPurchaseOrderVendorStipulationLine.setVendorStipulationAuthorEmployeeIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
270            aPurchaseOrderVendorStipulationLine.setVendorStipulationCreateDate(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate());
271    
272            return aPurchaseOrderVendorStipulationLine;
273        }
274    
275        public String getStatusChange() {
276            if (StringUtils.isNotEmpty(getPurchaseOrderDocument().getStatusChange())){
277                return getPurchaseOrderDocument().getStatusChange();
278            } else {
279                if (StringUtils.equals(getPurchaseOrderDocument().getStatusCode(),PurchaseOrderStatuses.IN_PROCESS)){
280                    return PurchaseOrderStatuses.IN_PROCESS;
281                } else if (StringUtils.equals(getPurchaseOrderDocument().getStatusCode(),PurchaseOrderStatuses.WAITING_FOR_DEPARTMENT)){
282                    return PurchaseOrderStatuses.WAITING_FOR_DEPARTMENT;
283                }else if (StringUtils.equals(getPurchaseOrderDocument().getStatusCode(),PurchaseOrderStatuses.WAITING_FOR_VENDOR)){
284                    return PurchaseOrderStatuses.WAITING_FOR_VENDOR;   
285                }else{
286                    return null;
287                }
288            }
289        }
290    
291        public void setStatusChange(String statusChange) {
292            getPurchaseOrderDocument().setStatusChange(statusChange);
293        }
294    
295        /**
296         * @see org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase#shouldMethodToCallParameterBeUsed(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
297         */
298        @Override
299        public boolean shouldMethodToCallParameterBeUsed(String methodToCallParameterName, String methodToCallParameterValue, HttpServletRequest request) {
300            List<String> methodToCallList = Arrays.asList(new String[]{"printPurchaseOrderPDFOnly", "printingRetransmitPoOnly", "printPoQuoteListOnly"});
301    
302            if (KNSConstants.DISPATCH_REQUEST_PARAMETER.equals(methodToCallParameterName) && methodToCallList.contains(methodToCallParameterValue)) {
303                return true;
304            }
305            return super.shouldMethodToCallParameterBeUsed(methodToCallParameterName, methodToCallParameterValue, request);
306        }
307    
308        @Override
309        public void populateHeaderFields(KualiWorkflowDocument workflowDocument) {
310            super.populateHeaderFields(workflowDocument);
311            if (ObjectUtils.isNotNull(getPurchaseOrderDocument().getPurapDocumentIdentifier())) {
312                String poIDstr = getPurchaseOrderDocument().getPurapDocumentIdentifier().toString();
313                if (getPurchaseOrderDocument().getNeedWarning()) {
314                    poIDstr += " UNAPPROVED";
315                }
316                getDocInfo().add(new HeaderField("DataDictionary.PurchaseOrderDocument.attributes.purapDocumentIdentifier", poIDstr));
317            }
318            else {
319                getDocInfo().add(new HeaderField("DataDictionary.PurchaseOrderDocument.attributes.purapDocumentIdentifier", "Not Available"));
320            }
321            if (ObjectUtils.isNotNull(getPurchaseOrderDocument().getStatus())) {
322                getDocInfo().add(new HeaderField("DataDictionary.PurchaseOrderDocument.attributes.statusCode", ((PurchaseOrderDocument) this.getDocument()).getStatus().getStatusDescription()));
323            }
324            else {
325                getDocInfo().add(new HeaderField("DataDictionary.PurchaseOrderDocument.attributes.statusCode", "Not Available"));
326            }
327        }
328        
329        /**
330         * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentFormBase#populate(javax.servlet.http.HttpServletRequest)
331         */
332        @Override
333        public void populate(HttpServletRequest request) {
334            PurchaseOrderDocument po = (PurchaseOrderDocument) this.getDocument();
335    
336            // call this to make sure it's refreshed from the database if need be since the populate setter doesn't do that
337            po.getDocumentBusinessObject();
338            
339            super.populate(request);
340            
341            if (ObjectUtils.isNotNull(po.getPurapDocumentIdentifier())) {
342                po.refreshDocumentBusinessObject();
343            }
344    
345            for (org.kuali.rice.kns.bo.Note note : (java.util.List<org.kuali.rice.kns.bo.Note>) po.getDocumentBusinessObject().getBoNotes()) {
346                note.refreshReferenceObject("attachment");
347            }        
348        }
349        
350        /**
351         * Processes validation rules having to do with any payment requests that the given purchase order may have. Specifically,
352         * validates that at least one payment request exists, and makes further checks about the status of such payment requests.
353         * 
354         * @param document A PurchaseOrderDocument
355         * @return True if the document passes all the validations.
356         */
357        protected boolean processPaymentRequestRulesForCanClose(PurchaseOrderDocument document) {
358            boolean valid = true;
359            // The PO must have at least one PREQ against it.
360            Integer poDocId = document.getPurapDocumentIdentifier();
361            List<PaymentRequestDocument> pReqs = SpringContext.getBean(PaymentRequestService.class).getPaymentRequestsByPurchaseOrderId(poDocId);
362            if (ObjectUtils.isNotNull(pReqs)) {
363                if (pReqs.size() == 0) {
364                    valid = false;
365                }
366                else {
367                    boolean checkInProcess = true;
368                    boolean hasInProcess = false;
369    
370                    for (PaymentRequestDocument pReq : pReqs) {
371                        // skip exception docs
372                        if (pReq.getDocumentHeader().getWorkflowDocument().stateIsException()) {
373                            continue;
374                        }
375                        // TODO NOTE for below, this could/should be changed to look at the first route level after full entry instead of
376                        // being tied to AwaitingFiscal (in case full entry is moved)
377                        // look for a doc that is currently routing, that will probably be the one that called this close if called from
378                        // preq (with close po box)
379                        if (StringUtils.equalsIgnoreCase(pReq.getStatusCode(), PaymentRequestStatuses.AWAITING_FISCAL_REVIEW) && !StringUtils.equalsIgnoreCase(pReq.getDocumentHeader().getWorkflowDocument().getCurrentRouteNodeNames(), PurapWorkflowConstants.PaymentRequestDocument.NodeDetailEnum.ACCOUNT_REVIEW.getName())) {
380                            // terminate the search since this close doc is probably being called by this doc, a doc should never be In
381                            // Process and enroute in any other case
382                            checkInProcess = false;
383                            break;
384                        }
385                        if (StringUtils.equalsIgnoreCase(pReq.getStatusCode(), PaymentRequestStatuses.IN_PROCESS)) {
386                            hasInProcess = true;
387                        }
388                    }
389                    if (checkInProcess && hasInProcess) {
390                        valid = false;
391                    }
392                }
393            }
394    
395            return valid;
396        }
397    
398        /**
399         * Determines whether to display the amend button for the purchase order document. The document status must be open, and the
400         * purchase order must be current and not pending, and the user must be in purchasing group. These are same as the conditions
401         * for displaying the payment hold button. In addition to these conditions, we also have to check that there is no In Process
402         * Payment Requests nor Credit Memos associated with the PO.
403         * 
404         * @return boolean true if the amend button can be displayed.
405         */
406        protected boolean canAmend() {
407            boolean can = SpringContext.getBean(PurchaseOrderService.class).isPurchaseOrderOpenForProcessing(getPurchaseOrderDocument());
408            
409            // check user authorization
410            if (can) {
411                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
412                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_AMENDMENT, GlobalVariables.getUserSession().getPerson());
413            }
414    
415            return can;
416        }
417        
418        /**
419         * Determines whether to display the void button for the purchase order document. Conditions:
420         * PO is in Pending Print status, or is in Open status and has no PREQs against it;
421         * PO's current indicator is true and pending indicator is false;
422         * and the user is a member of the purchasing group).
423         * 
424         * @return boolean true if the void button can be displayed.
425         */
426        protected boolean canVoid() {
427            // check PO status etc
428            boolean can = getPurchaseOrderDocument().isPurchaseOrderCurrentIndicator() && !getPurchaseOrderDocument().isPendingActionIndicator();
429                   
430            if (can) {
431                boolean pendingPrint = PurchaseOrderStatuses.PENDING_PRINT.equals(getPurchaseOrderDocument().getStatusCode());
432                boolean open = PurchaseOrderStatuses.OPEN.equals(getPurchaseOrderDocument().getStatusCode());
433                boolean errorCxml = PurchaseOrderStatuses.CXML_ERROR.equals(getPurchaseOrderDocument().getStatusCode());
434                boolean errorFax = PurchaseOrderStatuses.FAX_ERROR.equals(getPurchaseOrderDocument().getStatusCode());
435    
436                List<PaymentRequestView> preqViews = getPurchaseOrderDocument().getRelatedViews().getRelatedPaymentRequestViews();
437                boolean hasPaymentRequest = preqViews != null && preqViews.size() > 0;
438    
439                can = pendingPrint || (open && !hasPaymentRequest) || errorCxml || errorFax;
440            }
441    
442            // check user authorization
443            if (can) {
444                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
445                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_VOID, GlobalVariables.getUserSession().getPerson());
446            }
447    
448            return can;        
449        }
450        
451        /**
452         * Determines whether to display the close order button to close the purchase order document. Conditions:
453         * PO must be in Open status; must have at least one Payment Request in any status other than "In Process", 
454         * and the PO cannot have any Payment Requests in "In Process" status. 
455         * This button is available to all faculty/staff.
456         * 
457         * @return boolean true if the close order button can be displayed.
458         */
459        protected boolean canClose() {        
460            // check PO status etc
461            boolean can = PurchaseOrderStatuses.OPEN.equals(getPurchaseOrderDocument().getStatusCode());
462            can = can && getPurchaseOrderDocument().isPurchaseOrderCurrentIndicator() && !getPurchaseOrderDocument().isPendingActionIndicator();
463            can = can && processPaymentRequestRulesForCanClose(getPurchaseOrderDocument());
464            
465            // check user authorization
466            if (can) {
467                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
468                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_CLOSE, GlobalVariables.getUserSession().getPerson());
469            }
470    
471            return can;        
472        }
473    
474        /**
475         * Determines whether to display the open order button to reopen the purchase order document.
476         * Conditions: PO status is close, PO is current and not pending, and the user is in purchasing group.
477         * 
478         * @return boolean true if the reopen order button can be displayed.
479         */
480        protected boolean canReopen() {
481            // check PO status etc
482            boolean can = PurchaseOrderStatuses.CLOSED.equals(getPurchaseOrderDocument().getStatusCode());
483            can = can && getPurchaseOrderDocument().isPurchaseOrderCurrentIndicator() && !getPurchaseOrderDocument().isPendingActionIndicator();
484            
485            // check user authorization
486            if (can) {
487                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
488                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_REOPEN, GlobalVariables.getUserSession().getPerson());
489            }
490    
491            return can;
492        }
493        
494        /**
495         * Determines whether to display the payment hold buttons for the purchase order document.
496         * Conditions: PO status must be open, must be current and not pending, and the user must be in purchasing group.
497         * 
498         * @return boolean true if the payment hold button can be displayed.
499         */
500        protected boolean canHoldPayment() {
501            // check PO status etc
502            boolean can = PurchaseOrderStatuses.OPEN.equals(getPurchaseOrderDocument().getStatusCode());
503            can = can && getPurchaseOrderDocument().isPurchaseOrderCurrentIndicator() && !getPurchaseOrderDocument().isPendingActionIndicator();
504            
505            // check user authorization
506            if (can) {
507                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
508                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_PAYMENT_HOLD, GlobalVariables.getUserSession().getPerson());
509            }
510    
511            return can;        
512        }
513        
514        /**
515         * Determines whether to display the remove hold button for the purchase order document.
516         * Conditions are: PO status must be payment hold, must be current and not pending, and the user must be in purchasing group.
517         * 
518         * @return boolean true if the remove hold button can be displayed.
519         */
520        protected boolean canRemoveHold() {
521            // check PO status etc
522            boolean can = PurchaseOrderStatuses.PAYMENT_HOLD.equals(getPurchaseOrderDocument().getStatusCode());
523            can = can && getPurchaseOrderDocument().isPurchaseOrderCurrentIndicator() && !getPurchaseOrderDocument().isPendingActionIndicator();
524            
525            // check user authorization
526            if (can) {
527                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
528                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_REMOVE_HOLD, GlobalVariables.getUserSession().getPerson());
529            }
530    
531            return can;        
532        }
533            
534        /**
535         * Determines whether to display the retransmit button. Conditions: 
536         * PO status must be open, and must be current and not pending, and the last transmit date must not be null. 
537         * If the purchase order is an Automated Purchase Order (APO) and does not have any sensitive data set to true, 
538         * then any users can see the retransmit button, otherwise, only users in the purchasing group can see it.
539         * 
540         * @return boolean true if the retransmit button can be displayed.
541         */
542        protected boolean canRetransmit() {
543            // check PO status etc
544            boolean can = PurchaseOrderStatuses.OPEN.equals(getPurchaseOrderDocument().getStatusCode());
545            can = can && getPurchaseOrderDocument().isPurchaseOrderCurrentIndicator() && !getPurchaseOrderDocument().isPendingActionIndicator();
546            can = can && getPurchaseOrderDocument().getPurchaseOrderLastTransmitTimestamp() != null;
547            can = can && !PurapConstants.RequisitionSources.B2B.equals(getPurchaseOrderDocument().getRequisitionSourceCode());
548            can = can && !editingMode.containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.DISPLAY_RETRANSMIT_TAB);
549            
550            if (!can) {
551                return false;       
552            }
553            
554            // check user authorization
555            DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
556            if (getPurchaseOrderDocument().getPurchaseOrderAutomaticIndicator()) {
557                // for APO use authorization for PurchaseOrderRetransmitDocument, which is anybody
558                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_RETRANSMIT, GlobalVariables.getUserSession().getPerson());
559            }
560            else {
561                // for NON_APO use authorization for PurchaseOrderDocument, which is purchasing user
562                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER, GlobalVariables.getUserSession().getPerson());            
563            }
564            
565            return can;
566        }
567    
568        /**
569         * Determines whether to display the button to print the pdf on a retransmit document. 
570         * We're currently sharing the same button image as the button for creating a retransmit document but this may change someday. 
571         * This button should only appear on Retransmit Document. If it is an Automated Purchase Order (APO) 
572         * then any users can see this button, otherwise, only users in the purchasing group can see it.
573         * 
574         * @return boolean true if the print retransmit button can be displayed.
575         */
576        protected boolean canPrintRetransmit() {
577            // check PO status etc
578            boolean can = getPurchaseOrderDocument().getDocumentHeader().getWorkflowDocument().getDocumentType().equals(PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_RETRANSMIT_DOCUMENT);
579            can = can && editingMode.containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.DISPLAY_RETRANSMIT_TAB);
580            
581            if (can) {
582                // check user authorization: same as retransmit init, since whoever can init retransmit PO shall be able to print
583                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
584                if (getPurchaseOrderDocument().getPurchaseOrderAutomaticIndicator()) {
585                    // for APO use authorization for PurchaseOrderRetransmitDocument, which is anybody
586                    can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_RETRANSMIT, GlobalVariables.getUserSession().getPerson());
587                }
588                else {
589                    // for NON_APO use authorization for PurchaseOrderDocument, which is purchasing user
590                    can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER, GlobalVariables.getUserSession().getPerson());            
591                }
592            }
593          
594            return can;
595        }    
596        
597        /**
598         * Determines if a Split PO Document can be created from this purchase order. Conditions: 
599         * The parent PO status is either "In Process" or "Awaiting Purchasing Review"; requisition source is not B2B; has at least 2 items, 
600         * and PO is not in the process of being split; user must be in purchasing group.
601         * 
602         * @return boolean true if the split PO button can be displayed.
603         */
604        protected boolean canSplitPo() {
605            // PO must be in either "In Process" or "Awaiting Purchasing Review"
606            boolean can = PurchaseOrderStatuses.IN_PROCESS.equals(getPurchaseOrderDocument().getStatusCode());
607            can = can && !getPurchaseOrderDocument().getDocumentHeader().getWorkflowDocument().stateIsEnroute(); 
608            can = can || PurchaseOrderStatuses.AWAIT_PURCHASING_REVIEW.equals(getPurchaseOrderDocument().getStatusCode());
609            
610            // can't split a SplitPO Document, according to new specs
611            can = can && !(getPurchaseOrderDocument() instanceof PurchaseOrderSplitDocument); 
612            
613            // can't initiate another split during the splitting process. 
614            can = can && !editingMode.containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.SPLITTING_ITEM_SELECTION);
615            
616            // Requisition Source must not be B2B.
617            can = can && !getPurchaseOrderDocument().getRequisitionSourceCode().equals(PurapConstants.RequisitionSources.B2B);
618            
619            // PO must have more than one line item.
620            if (can) {
621                List<PurApItem> items = (List<PurApItem>)getPurchaseOrderDocument().getItems();
622                int itemsBelowTheLine = PurApItemUtils.countBelowTheLineItems(items);
623                can = items.size() - itemsBelowTheLine > 1;
624            }
625            
626            // check user authorization
627            if (can) {
628                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
629                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_SPLIT, GlobalVariables.getUserSession().getPerson());
630            }
631                  
632            return can;
633        }
634        
635        /**
636         * Determines whether the PO is in a status that signifies it has enough information to generate a Split PO.
637         * 
638         * @return  True if the PO can continue to be split.
639         */
640        protected boolean canContinuePoSplit() {
641            boolean can = editingMode.containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.SPLITTING_ITEM_SELECTION);
642            
643            // check user authorization
644            if (can) {
645                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
646                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER_SPLIT, GlobalVariables.getUserSession().getPerson());
647            }
648                  
649            return can;        
650        }
651        
652        /**
653         * Determines if a line item receiving document can be created for the purchase order.
654         * 
655         * @return boolean true if the receiving document button can be displayed.
656         */
657        protected boolean canCreateReceiving() {       
658            // check PO status and item info 
659            boolean can = SpringContext.getBean(ReceivingService.class).canCreateLineItemReceivingDocument(getPurchaseOrderDocument());
660            
661            // check user authorization
662            if (can) {
663                DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(getPurchaseOrderDocument());
664                can = documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.LINE_ITEM_RECEIVING, GlobalVariables.getUserSession().getPerson());
665            }
666                  
667            return can;
668        }
669        
670        /**
671         * Creates a MAP for all the buttons to appear on the Purchase Order Form, and sets the attributes of these buttons.
672         * 
673         * @return the button map created.
674         */
675        protected Map<String, ExtraButton> createButtonsMap() {
676            HashMap<String, ExtraButton> result = new HashMap<String, ExtraButton>();
677            
678            // Retransmit button
679            ExtraButton retransmitButton = new ExtraButton();
680            retransmitButton.setExtraButtonProperty("methodToCall.retransmitPo");
681            retransmitButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_retransmit.gif");
682            retransmitButton.setExtraButtonAltText("Retransmit");
683    
684            // Printing Retransmit button
685            ExtraButton printingRetransmitButton = new ExtraButton();
686            printingRetransmitButton.setExtraButtonProperty("methodToCall.printingRetransmitPo");
687            printingRetransmitButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_retransmit.gif");
688            printingRetransmitButton.setExtraButtonAltText("PrintingRetransmit");
689    
690            // Printing Preview button
691            ExtraButton printingPreviewButton = new ExtraButton();
692            printingPreviewButton.setExtraButtonProperty("methodToCall.printingPreviewPo");
693            printingPreviewButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_previewpf.gif");
694            printingPreviewButton.setExtraButtonAltText("PrintingPreview");
695            
696            // Print button
697            ExtraButton printButton = new ExtraButton();
698            printButton.setExtraButtonProperty("methodToCall.firstTransmitPrintPo");
699            printButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_print.gif");
700            printButton.setExtraButtonAltText("Print");
701    
702            // Reopen PO button
703            ExtraButton reopenButton = new ExtraButton();
704            reopenButton.setExtraButtonProperty("methodToCall.reopenPo");
705            reopenButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_openorder.gif");
706            reopenButton.setExtraButtonAltText("Reopen");
707    
708            // Close PO button
709            ExtraButton closeButton = new ExtraButton();
710            closeButton.setExtraButtonProperty("methodToCall.closePo");
711            closeButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_closeorder.gif");
712            closeButton.setExtraButtonAltText("Close PO");
713    
714            // Void PO button
715            ExtraButton voidButton = new ExtraButton();
716            voidButton.setExtraButtonProperty("methodToCall.voidPo");
717            voidButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_voidorder.gif");
718            voidButton.setExtraButtonAltText("Void PO");
719    
720            // Payment Hold PO button
721            ExtraButton paymentHoldButton = new ExtraButton();
722            paymentHoldButton.setExtraButtonProperty("methodToCall.paymentHoldPo");
723            paymentHoldButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_paymenthold.gif");
724            paymentHoldButton.setExtraButtonAltText("Payment Hold");
725    
726            // Amend button
727            ExtraButton amendButton = new ExtraButton();
728            amendButton.setExtraButtonProperty("methodToCall.amendPo");
729            amendButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_amend.gif");
730            amendButton.setExtraButtonAltText("Amend");
731    
732            // Remove Hold button
733            ExtraButton removeHoldButton = new ExtraButton();
734            removeHoldButton.setExtraButtonProperty("methodToCall.removeHoldPo");
735            removeHoldButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_removehold.gif");
736            removeHoldButton.setExtraButtonAltText("Remove Hold");
737    
738            // Resend PO Cxml button
739            ExtraButton resendPoCxmlButton = new ExtraButton();
740            resendPoCxmlButton.setExtraButtonProperty("methodToCall.resendPoCxml");
741            resendPoCxmlButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_resendpo.gif");
742            resendPoCxmlButton.setExtraButtonAltText("Resend PO CXML");
743    
744            // Receiving button
745            ExtraButton receivingButton = new ExtraButton();
746            receivingButton.setExtraButtonProperty("methodToCall.createReceivingLine");
747            receivingButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_receiving.gif");
748            receivingButton.setExtraButtonAltText("Receiving");
749            
750            // Split PO button
751            ExtraButton splitPoButton = new ExtraButton();
752            splitPoButton.setExtraButtonProperty("methodToCall.splitPo");
753            splitPoButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_splitorder.gif");
754            splitPoButton.setExtraButtonAltText("Split this PO");
755            
756            // Continue button
757            ExtraButton continueButton = new ExtraButton();
758            continueButton.setExtraButtonProperty("methodToCall.continuePurchaseOrderSplit");
759            continueButton.setExtraButtonSource("${" + KFSConstants.RICE_EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_continue.gif");
760            continueButton.setExtraButtonAltText("Continue");
761            
762            // Cancel Split button
763            ExtraButton cancelSplitButton = new ExtraButton();
764            cancelSplitButton.setExtraButtonProperty("methodToCall.cancelPurchaseOrderSplit");
765            cancelSplitButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_cancelsplit.gif");
766            cancelSplitButton.setExtraButtonAltText("Cancel Splitting the PO");
767            
768            // Assign Sensitive Data button
769            ExtraButton assignSensitiveDataButton = new ExtraButton();
770            assignSensitiveDataButton.setExtraButtonProperty("methodToCall.assignSensitiveData");
771            assignSensitiveDataButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_sensitivedata.gif ");
772            assignSensitiveDataButton.setExtraButtonAltText("Assign sensitive data to the PO");
773            
774            // Submit Sensitive Data Assignment button
775            ExtraButton submitSensitiveDataButton = new ExtraButton();
776            submitSensitiveDataButton.setExtraButtonProperty("methodToCall.submitSensitiveData");
777            submitSensitiveDataButton.setExtraButtonSource("${" + KFSConstants.RICE_EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_submit.gif");
778            submitSensitiveDataButton.setExtraButtonAltText("Submit sensitive data assignment");
779            
780            // Cancel Sensitive Data Assignment button
781            ExtraButton cancelSensitiveDataButton = new ExtraButton();
782            cancelSensitiveDataButton.setExtraButtonProperty("methodToCall.cancelSensitiveData");
783            cancelSensitiveDataButton.setExtraButtonSource("${" + KFSConstants.RICE_EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_cancel.gif");
784            cancelSensitiveDataButton.setExtraButtonAltText("Cancel sensitive data assignment");
785            
786            result.put(retransmitButton.getExtraButtonProperty(), retransmitButton);
787            result.put(printingRetransmitButton.getExtraButtonProperty(), printingRetransmitButton);
788            result.put(printingPreviewButton.getExtraButtonProperty(), printingPreviewButton);
789            result.put(printButton.getExtraButtonProperty(), printButton);
790            result.put(reopenButton.getExtraButtonProperty(), reopenButton);
791            result.put(closeButton.getExtraButtonProperty(), closeButton);
792            result.put(voidButton.getExtraButtonProperty(), voidButton);
793            result.put(paymentHoldButton.getExtraButtonProperty(), paymentHoldButton);
794            result.put(amendButton.getExtraButtonProperty(), amendButton);
795            result.put(removeHoldButton.getExtraButtonProperty(), removeHoldButton);
796            result.put(receivingButton.getExtraButtonProperty(), receivingButton);
797            result.put(splitPoButton.getExtraButtonProperty(), splitPoButton);
798            result.put(continueButton.getExtraButtonProperty(), continueButton);
799            result.put(cancelSplitButton.getExtraButtonProperty(), cancelSplitButton);
800            result.put(assignSensitiveDataButton.getExtraButtonProperty(), assignSensitiveDataButton);
801            result.put(submitSensitiveDataButton.getExtraButtonProperty(), submitSensitiveDataButton);
802            result.put(cancelSensitiveDataButton.getExtraButtonProperty(), cancelSensitiveDataButton);
803            result.put(resendPoCxmlButton.getExtraButtonProperty(), resendPoCxmlButton);
804            
805            return result;
806        }
807    
808        /**
809         * Override the superclass method to add appropriate buttons for
810         * PurchaseOrderDocument.
811         * 
812         * @see org.kuali.rice.kns.web.struts.form.KualiForm#getExtraButtons()
813         */
814        @Override
815        public List<ExtraButton> getExtraButtons() {
816            super.getExtraButtons();        
817            Map buttonsMap = createButtonsMap();   
818            
819            if (getEditingMode().containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.ASSIGN_SENSITIVE_DATA)) {
820                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.assignSensitiveData"));
821                if (getPurchaseOrderDocument().getAssigningSensitiveData()) {
822                    // no other extra buttons except the following shall appear on "Assign Sensitive Data" page
823                    // and these buttons use the same permissions as the "Assign Sensitive Data" button
824                    extraButtons.clear();
825                    extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.submitSensitiveData"));
826                    extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.cancelSensitiveData"));
827                    return extraButtons;
828                }
829            }
830    
831            if (getEditingMode().containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.PREVIEW_PRINT_PURCHASE_ORDER)) {
832                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.printingPreviewPo"));
833            }
834    
835            if (getEditingMode().containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.PRINT_PURCHASE_ORDER)) {
836                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.firstTransmitPrintPo"));
837            }
838    
839            if (getEditingMode().containsKey(PurapAuthorizationConstants.PurchaseOrderEditMode.RESEND_PURCHASE_ORDER)) {
840                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.resendPoCxml"));
841            }
842    
843            if (canRetransmit()) {
844                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.retransmitPo"));
845            }
846    
847            if (canPrintRetransmit()) {
848                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.printingRetransmitPo"));
849            }
850    
851            if (canReopen()) {
852                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.reopenPo"));
853            }
854    
855            if (canClose()) {
856                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.closePo"));
857            }
858    
859            if (canHoldPayment()) {
860                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.paymentHoldPo"));
861            }
862    
863            if (canAmend()) {
864                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.amendPo"));
865            }
866    
867            if (canVoid()) {
868                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.voidPo"));
869            }
870    
871            if (canRemoveHold()) {
872                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.removeHoldPo"));
873            }
874    
875            if (canCreateReceiving()) {
876                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.createReceivingLine"));
877            }
878    
879            if (canSplitPo()) {
880                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.splitPo"));
881            }
882    
883            if (canContinuePoSplit()) {
884                extraButtons.clear();
885                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.continuePurchaseOrderSplit"));
886                extraButtons.add((ExtraButton) buttonsMap.get("methodToCall.cancelPurchaseOrderSplit"));
887            }
888            
889            return extraButtons;
890        }
891    
892    }
893