001/*
002 * Copyright 2008-2009 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 */
016package org.kuali.ole.module.purap.document.authorization;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.ole.module.purap.PurapAuthorizationConstants.InvoiceEditMode;
020import org.kuali.ole.module.purap.PurapConstants.InvoiceStatuses;
021import org.kuali.ole.module.purap.PurapConstants.PurchaseOrderStatuses;
022import org.kuali.ole.module.purap.PurapParameterConstants;
023import org.kuali.ole.module.purap.businessobject.InvoiceItem;
024import org.kuali.ole.module.purap.document.InvoiceDocument;
025import org.kuali.ole.module.purap.document.service.PurapService;
026import org.kuali.ole.sys.OLEConstants;
027import org.kuali.ole.sys.OleAuthorizationConstants;
028import org.kuali.ole.sys.context.SpringContext;
029import org.kuali.ole.sys.service.FinancialSystemWorkflowHelperService;
030import org.kuali.ole.sys.service.impl.OleParameterConstants;
031import org.kuali.rice.coreservice.framework.parameter.ParameterService;
032import org.kuali.rice.kew.api.WorkflowDocument;
033import org.kuali.rice.kew.api.action.ActionType;
034import org.kuali.rice.kew.api.action.ValidActions;
035import org.kuali.rice.krad.document.Document;
036import org.kuali.rice.krad.util.GlobalVariables;
037import org.kuali.rice.krad.util.ObjectUtils;
038
039import java.util.ArrayList;
040import java.util.Iterator;
041import java.util.List;
042import java.util.Set;
043
044
045public class InvoiceDocumentPresentationController extends PurchasingAccountsPayableDocumentPresentationController {
046
047    Boolean canHold;
048    Boolean canRequestCancel;
049    Boolean canEditPreExtraction;
050
051    @Override
052    public boolean canSave(Document document) {
053        InvoiceDocument invoiceDocument = (InvoiceDocument) document;
054
055        if (!StringUtils.equalsIgnoreCase(invoiceDocument.getDocumentHeader().getWorkflowDocument().getStatus().name(), InvoiceStatuses.APPDOC_INITIATE)
056               && !StringUtils.equalsIgnoreCase(invoiceDocument.getDocumentHeader().getWorkflowDocument().getStatus().name(), OLEConstants.OleInvoice.INVOICE_SAVED)) {
057            return false;
058        }
059
060        if (canEditPreExtraction(invoiceDocument)) {
061            return true;
062        }
063
064        return super.canSave(document);
065    }
066
067    @Override
068    public boolean canReload(Document document) {
069        InvoiceDocument invoiceDocument = (InvoiceDocument) document;
070
071        if (StringUtils.equals(invoiceDocument.getApplicationDocumentStatus(), InvoiceStatuses.APPDOC_INITIATE)) {
072            return false;
073        }
074
075        if (canEditPreExtraction(invoiceDocument)) {
076            return true;
077        }
078
079        return super.canReload(document);
080    }
081
082    @Override
083    public boolean canCancel(Document document) {
084        //controlling the cancel button through getExtraButtons in InvoiceForm
085        return false;
086    }
087
088    @Override
089    public boolean canApprove(Document document) {
090        InvoiceDocument invoiceDocument = (InvoiceDocument) document;
091
092        if (invoiceDocument.isInvoiceCancelIndicator() || invoiceDocument.isHoldIndicator()) {
093            return false;
094        }
095
096        return super.canApprove(document);
097    }
098
099    @Override
100    public boolean canCopy (Document document) {
101        InvoiceDocument invoiceDocument = (InvoiceDocument) document;
102        if (super.canCopy(document)) {
103            if (!StringUtils.equalsIgnoreCase(invoiceDocument.getDocumentHeader().getWorkflowDocument().getStatus().name(), InvoiceStatuses.APPDOC_INITIATE)) {
104                return true;
105            }
106        }
107        return false;
108
109    }
110
111    @Override
112    public boolean canDisapprove(Document document) {
113
114        WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
115        if (workflowDocument.isEnroute()) {
116            ValidActions validActions = workflowDocument.getValidActions();
117            return validActions.getValidActions().contains(ActionType.DISAPPROVE);
118        }
119
120        return super.canDisapprove(document);
121    }
122
123    /**
124     * @see org.kuali.rice.krad.document.DocumentPresentationControllerBase#canEdit(org.kuali.rice.krad.document.Document)
125     */
126    @Override
127    public boolean canEdit(Document document) {
128        InvoiceDocument invoiceDocument = (InvoiceDocument) document;
129        boolean fullDocEntryCompleted = SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(invoiceDocument);
130
131        // if the hold or cancel indicator is true, don't allow editing
132        if (invoiceDocument.isHoldIndicator() || invoiceDocument.isInvoiceCancelIndicator()) {
133            return false;
134        }
135        if (fullDocEntryCompleted) {
136            //  after fullDocEntry is completed, only fiscal officer reviewers can edit
137            if (invoiceDocument.isDocumentStoppedInRouteNode(InvoiceStatuses.NODE_ACCOUNT_REVIEW)) {
138                return true;
139            }
140            return false;
141        } else {
142            //before fullDocEntry is completed, document can be edited (could be preroute or enroute)
143            return true;
144        }
145    }
146
147    /**
148     * @see org.kuali.rice.kns.document.authorization.TransactionalDocumentPresentationControllerBase#getEditModes(org.kuali.rice.krad.document.Document)
149     */
150    @Override
151    public Set<String> getEditModes(Document document) {
152        Set<String> editModes = super.getEditModes(document);
153
154        InvoiceDocument invoiceDocument = (InvoiceDocument) document;
155
156        if (canProcessorCancel(invoiceDocument)) {
157            editModes.add(InvoiceEditMode.ACCOUNTS_PAYABLE_PROCESSOR_CANCEL);
158        }
159
160        if (canManagerCancel(invoiceDocument)) {
161            editModes.add(InvoiceEditMode.ACCOUNTS_PAYABLE_MANAGER_CANCEL);
162        }
163
164        if (canHold(invoiceDocument)) {
165            editModes.add(InvoiceEditMode.HOLD);
166        }
167
168        if (canRequestCancel(invoiceDocument)) {
169            editModes.add(InvoiceEditMode.REQUEST_CANCEL);
170        }
171
172        if (canRemoveHold(invoiceDocument)) {
173            editModes.add(InvoiceEditMode.REMOVE_HOLD);
174        }
175
176        if (canRemoveRequestCancel(invoiceDocument)) {
177            editModes.add(InvoiceEditMode.REMOVE_REQUEST_CANCEL);
178        }
179
180        if (canProcessorInit(invoiceDocument)) {
181            editModes.add(InvoiceEditMode.DISPLAY_INIT_TAB);
182        }
183
184        if (ObjectUtils.isNotNull(invoiceDocument.getVendorHeaderGeneratedIdentifier())) {
185            editModes.add(InvoiceEditMode.LOCK_VENDOR_ENTRY);
186        }
187
188        if (SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(invoiceDocument)) {
189            editModes.add(InvoiceEditMode.FULL_DOCUMENT_ENTRY_COMPLETED);
190        }
191        //else if (ObjectUtils.isNotNull(invoiceDocument.getPurchaseOrderDocument()) && PurchaseOrderStatuses.APPDOC_OPEN.equals(invoiceDocument.getPurchaseOrderDocument().getApplicationDocumentStatus())) {
192          /*
193            String documentTypeName = OLEConstants.FinancialDocumentTypeCodes.PAYMENT_REQUEST;
194            String nameSpaceCode = OLEConstants.CoreModuleNamespaces.SELECT;
195
196            AttributeSet permissionDetails = new AttributeSet();
197            permissionDetails.put(KimAttributes.DOCUMENT_TYPE_NAME,documentTypeName);
198
199            boolean canClosePO = KIMServiceLocator.getIdentityManagementService().hasPermission(GlobalVariables.getUserSession().getPerson().getPrincipalId(), nameSpaceCode,
200                    OLEConstants.OleInvoice.CAN_CLOSE_PO, permissionDetails);
201            if(canClosePO) {
202            editModes.add(InvoiceEditMode.ALLOW_CLOSE_PURCHASE_ORDER);
203            }*/
204        //  editModes.add(InvoiceEditMode.ALLOW_CLOSE_PURCHASE_ORDER);
205        // }
206
207        //FIXME hjs: alter to restrict what AP shouldn't be allowed to edit
208        if (canEditPreExtraction(invoiceDocument)) {
209            editModes.add(InvoiceEditMode.EDIT_PRE_EXTRACT);
210        }
211
212        // See if purap tax is enabled
213        boolean salesTaxInd = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND);
214        if (salesTaxInd) {
215            editModes.add(InvoiceEditMode.PURAP_TAX_ENABLED);
216
217            if (invoiceDocument.isUseTaxIndicator()) {
218                // if use tax, don't allow editing of tax fields
219                editModes.add(InvoiceEditMode.LOCK_TAX_AMOUNT_ENTRY);
220            } else {
221                // display the "clear all taxes" button if doc is not using use tax
222                editModes.add(InvoiceEditMode.CLEAR_ALL_TAXES);
223
224            }
225        }
226
227        // tax area tab is editable while waiting for tax review
228        if (invoiceDocument.isDocumentStoppedInRouteNode(InvoiceStatuses.NODE_VENDOR_TAX_REVIEW)) {
229            editModes.add(InvoiceEditMode.TAX_AREA_EDITABLE);
230        }
231
232        if (PurchaseOrderStatuses.APPDOC_AWAIT_TAX_REVIEW
233                .equals(invoiceDocument.getApplicationDocumentStatus())) {
234            editModes.add(InvoiceEditMode.TAX_AREA_EDITABLE);
235        }
236
237
238        // the tax tab is viewable to everyone after tax is approved
239        if (InvoiceStatuses.APPDOC_DEPARTMENT_APPROVED.equals(invoiceDocument.getApplicationDocumentStatus()) &&
240                // if and only if the invoice has gone through tax review would TaxClassificationCode be non-empty
241                !StringUtils.isEmpty(invoiceDocument.getTaxClassificationCode())) {
242            editModes.add(InvoiceEditMode.TAX_INFO_VIEWABLE);
243        }
244
245        if (invoiceDocument.isDocumentStoppedInRouteNode(InvoiceStatuses.NODE_ACCOUNT_REVIEW)) {
246            // remove FULL_ENTRY because FO cannot edit rest of doc; only their own acct lines
247            editModes.add(InvoiceEditMode.RESTRICT_FISCAL_ENTRY);
248
249            // only do line item check if the hold/cancel indicator is false, otherwise document editing should be turned off.
250            if (!invoiceDocument.isHoldIndicator() && !invoiceDocument.isInvoiceCancelIndicator()) {
251                List lineList = new ArrayList();
252                for (Iterator iter = invoiceDocument.getItems().iterator(); iter.hasNext(); ) {
253                    InvoiceItem item = (InvoiceItem) iter.next();
254                    lineList.addAll(item.getSourceAccountingLines());
255                    // If FO has deleted the last accounting line for an item, set entry mode to full so they can add another one
256                    if (item.getItemType().isLineItemIndicator() && item.getSourceAccountingLines().size() == 0) {
257                        editModes.add(OleAuthorizationConstants.TransactionalEditMode.EXPENSE_ENTRY);
258                    }
259                }
260            }
261        }
262
263        // Remove editBank edit mode if the document has been extracted
264        if (invoiceDocument.isExtracted()) {
265            editModes.remove(OLEConstants.BANK_ENTRY_EDITABLE_EDITING_MODE);
266        }
267
268        return editModes;
269    }
270
271    protected boolean canProcessorInit(InvoiceDocument invoiceDocument) {
272        // if Payment Request is in INITIATE status or NULL returned from getAppDocStatus
273        String status = invoiceDocument.getApplicationDocumentStatus();
274        if (StringUtils.equals(status, InvoiceStatuses.APPDOC_INITIATE)) {
275            return true;
276        }
277        return false;
278    }
279
280
281    protected boolean canProcessorCancel(InvoiceDocument invoiceDocument) {
282        // if Payment Request is in INITIATE status, user cannot cancel doc
283        if (canProcessorInit(invoiceDocument)) {
284            return false;
285        }
286
287        String docStatus = invoiceDocument.getApplicationDocumentStatus();
288        boolean requestCancelIndicator = invoiceDocument.getInvoiceCancelIndicator();
289        boolean holdIndicator = invoiceDocument.isHoldIndicator();
290        boolean extracted = invoiceDocument.isExtracted();
291
292        boolean preroute =
293                InvoiceStatuses.APPDOC_IN_PROCESS.equals(docStatus) ||
294                        InvoiceStatuses.APPDOC_AWAITING_ACCOUNTS_PAYABLE_REVIEW.equals(docStatus);
295        boolean enroute =
296                InvoiceStatuses.APPDOC_AWAITING_SUB_ACCT_MGR_REVIEW.equals(docStatus) ||
297                        InvoiceStatuses.APPDOC_AWAITING_FISCAL_REVIEW.equals(docStatus) ||
298                        InvoiceStatuses.APPDOC_AWAITING_ORG_REVIEW.equals(docStatus) ||
299                        InvoiceStatuses.APPDOC_AWAITING_PAYMENT_REVIEW.equals(docStatus)
300                        ||
301                        InvoiceStatuses.APPDOC_AWAITING_TAX_REVIEW.equals(docStatus);
302        boolean postroute =
303                InvoiceStatuses.APPDOC_DEPARTMENT_APPROVED.equals(docStatus) ||
304                        InvoiceStatuses.APPDOC_AUTO_APPROVED.equals(docStatus);
305
306        boolean can = false;
307        if (InvoiceStatuses.STATUSES_PREROUTE.contains(docStatus)) {
308            can = true;
309        } else if (InvoiceStatuses.STATUSES_ENROUTE.contains(docStatus)) {
310            can = requestCancelIndicator;
311        } else if (InvoiceStatuses.STATUSES_POSTROUTE.contains(docStatus)) {
312            can = !requestCancelIndicator && !holdIndicator && !extracted;
313        }
314
315        return can;
316    }
317
318    protected boolean canManagerCancel(InvoiceDocument invoiceDocument) {
319        // if Payment Request is in INITIATE status, user cannot cancel doc
320        if (canProcessorInit(invoiceDocument)) {
321            return false;
322        }
323
324        String docStatus = invoiceDocument.getApplicationDocumentStatus();
325        boolean requestCancelIndicator = invoiceDocument.getInvoiceCancelIndicator();
326        boolean holdIndicator = invoiceDocument.isHoldIndicator();
327        boolean extracted = invoiceDocument.isExtracted();
328
329        boolean preroute =
330                InvoiceStatuses.APPDOC_IN_PROCESS.equals(docStatus) ||
331                        InvoiceStatuses.APPDOC_AWAITING_ACCOUNTS_PAYABLE_REVIEW.equals(docStatus);
332        boolean enroute =
333                InvoiceStatuses.APPDOC_AWAITING_SUB_ACCT_MGR_REVIEW.equals(docStatus) ||
334                        InvoiceStatuses.APPDOC_AWAITING_FISCAL_REVIEW.equals(docStatus) ||
335                        InvoiceStatuses.APPDOC_AWAITING_ORG_REVIEW.equals(docStatus) ||
336                        InvoiceStatuses.APPDOC_AWAITING_PAYMENT_REVIEW.equals(docStatus)
337                        ||
338                        InvoiceStatuses.APPDOC_AWAITING_TAX_REVIEW.equals(docStatus);
339        boolean postroute =
340                InvoiceStatuses.APPDOC_DEPARTMENT_APPROVED.equals(docStatus) ||
341                        InvoiceStatuses.APPDOC_AUTO_APPROVED.equals(docStatus);
342
343        boolean can = false;
344        if (InvoiceStatuses.STATUSES_PREROUTE.contains(docStatus) ||
345                InvoiceStatuses.STATUSES_ENROUTE.contains(docStatus)) {
346            can = true;
347        } else if (InvoiceStatuses.STATUSES_POSTROUTE.contains(docStatus)) {
348            can = !requestCancelIndicator && !holdIndicator && !extracted;
349        }
350
351        return can;
352    }
353
354    /**
355     * Determines whether the Invoice Hold button shall be available. Conditions:
356     * - Payment Request is not already on hold, and
357     * - Payment Request is not already being requested to be canceled, and
358     * - Payment Request has not already been extracted to PDP, and
359     * - Payment Request status is not in the list of "STATUSES_DISALLOWING_HOLD" or document is being adhoc routed; and
360     *
361     * @return True if the document state allows placing the Payment Request on hold.
362     */
363    protected boolean canHold(InvoiceDocument invoiceDocument) {
364        if (canHold == null) {
365
366            boolean can = !invoiceDocument.isHoldIndicator()
367                    && !invoiceDocument.isInvoiceCancelIndicator()
368                    && !invoiceDocument.isExtracted();
369            if (can) {
370                can = SpringContext.getBean(FinancialSystemWorkflowHelperService.class)
371                        .isAdhocApprovalRequestedForPrincipal(
372                                invoiceDocument.getFinancialSystemDocumentHeader().getWorkflowDocument(),
373                                GlobalVariables.getUserSession().getPrincipalId());
374                can = can
375                        || !InvoiceStatuses.STATUSES_DISALLOWING_HOLD.contains(invoiceDocument
376                        .getApplicationDocumentStatus());
377            }
378            canHold = can;
379        }
380
381        return canHold;
382    }
383
384    /**
385     * Determines whether the Request Cancel Invoice button shall be available. Conditions:
386     * - Payment Request is not already on hold, and
387     * - Payment Request is not already being requested to be canceled, and
388     * - Payment Request has not already been extracted to PDP, and
389     * - Payment Request status is not in the list of "STATUSES_DISALLOWING_REQUEST_CANCEL" or document is being adhoc routed; and
390     *
391     * @return True if the document state allows placing the request that the Payment Request be canceled.
392     */
393    protected boolean canRequestCancel(InvoiceDocument invoiceDocument) {
394        if (canRequestCancel == null) {
395            boolean can = !invoiceDocument.isInvoiceCancelIndicator()
396                    && !invoiceDocument.isHoldIndicator() && !invoiceDocument.isExtracted();
397            if (can) {
398                can = SpringContext.getBean(FinancialSystemWorkflowHelperService.class)
399                        .isAdhocApprovalRequestedForPrincipal(
400                                invoiceDocument.getFinancialSystemDocumentHeader().getWorkflowDocument(),
401                                GlobalVariables.getUserSession().getPrincipalId());
402                can = can
403                        || !InvoiceStatuses.STATUSES_DISALLOWING_REQUEST_CANCEL.contains(invoiceDocument
404                        .getApplicationDocumentStatus());
405            }
406            canRequestCancel = can;
407        }
408        return canRequestCancel;
409    }
410
411    /**
412     * Determines whether the Remove Hold button shall be available. Conditions:
413     * - the hold indicator is set to true
414     * <p/>
415     * Because the state of the Payment Request cannot be changed while the document is on hold,
416     * we should not have to check the state of the document to remove the hold.
417     * For example, the document should not be allowed to be approved or extracted while on hold.
418     *
419     * @return True if the document state allows removing the Payment Request from hold.
420     */
421    protected boolean canRemoveHold(InvoiceDocument invoiceDocument) {
422        return invoiceDocument.isHoldIndicator();
423    }
424
425    /**
426     * Determines whether the Remove Request Cancel button shall be available. Conditions:
427     * - the request cancel indicator is set to true;  and
428     * <p/>
429     * Because the state of the Payment Request cannot be changed while the document is set to request cancel,
430     * we should not have to check the state of the document to remove the request cancel.
431     * For example, the document should not be allowed to be approved or extracted while set to request cancel.
432     *
433     * @return True if the document state allows removing a request that the Payment Request be canceled.
434     */
435    protected boolean canRemoveRequestCancel(InvoiceDocument invoiceDocument) {
436        return invoiceDocument.isInvoiceCancelIndicator();
437    }
438
439    protected boolean canEditPreExtraction(InvoiceDocument invoiceDocument) {
440        if (canEditPreExtraction == null) {
441            boolean can = (!invoiceDocument.isExtracted()
442                    && !SpringContext.getBean(FinancialSystemWorkflowHelperService.class)
443                    .isAdhocApprovalRequestedForPrincipal(
444                            invoiceDocument.getFinancialSystemDocumentHeader().getWorkflowDocument(),
445                            GlobalVariables.getUserSession().getPrincipalId()) && !InvoiceStatuses.CANCELLED_STATUSES
446                    .contains(invoiceDocument.getApplicationDocumentStatus()));
447            canEditPreExtraction = can;
448        }
449        return canEditPreExtraction;
450    }
451
452}