View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.document.validation.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.ole.module.purap.PurapConstants;
20  import org.kuali.ole.module.purap.PurapConstants.PRQSDocumentsStrings;
21  import org.kuali.ole.module.purap.PurapKeyConstants;
22  import org.kuali.ole.module.purap.PurapParameterConstants;
23  import org.kuali.ole.module.purap.document.AccountsPayableDocument;
24  import org.kuali.ole.module.purap.document.InvoiceDocument;
25  import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument;
26  import org.kuali.ole.module.purap.document.service.InvoiceService;
27  import org.kuali.ole.module.purap.document.service.PurapService;
28  import org.kuali.ole.module.purap.document.validation.event.AttributedExpiredAccountWarningEvent;
29  import org.kuali.ole.module.purap.document.validation.event.AttributedTradeInWarningEvent;
30  import org.kuali.ole.sys.OLEConstants;
31  import org.kuali.ole.sys.OLEKeyConstants;
32  import org.kuali.ole.sys.context.SpringContext;
33  import org.kuali.ole.sys.service.UniversityDateService;
34  import org.kuali.ole.sys.service.impl.OleParameterConstants;
35  import org.kuali.rice.core.api.config.property.ConfigurationService;
36  import org.kuali.rice.core.web.format.CurrencyFormatter;
37  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
38  import org.kuali.rice.krad.document.Document;
39  import org.kuali.rice.krad.service.KualiRuleService;
40  
41  /**
42   * Business pre rule(s) applicable to Invoice documents.
43   */
44  public class InvoiceDocumentPreRules extends AccountsPayableDocumentPreRulesBase {
45  
46      /**
47       * Default Constructor
48       */
49      public InvoiceDocumentPreRules() {
50          super();
51      }
52  
53      /**
54       * Main hook point to perform rules check.
55       *
56       * @see org.kuali.rice.kns.rules.PromptBeforeValidationBase#doPrompts(org.kuali.rice.krad.document.Document)
57       */
58      @Override
59      public boolean doPrompts(Document document) {
60          boolean preRulesOK = true;
61  
62          InvoiceDocument invoice = (InvoiceDocument) document;
63          if ((!SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(invoice)) || (StringUtils.equals(invoice.getApplicationDocumentStatus(), PurapConstants.InvoiceStatuses.APPDOC_AWAITING_ACCOUNTS_PAYABLE_REVIEW))) {
64              if (!confirmPayDayNotOverThresholdDaysAway(invoice)) {
65                  return false;
66              }
67              if (!confirmUnusedTradeIn(invoice)) {
68                  return false;
69              }
70              if (!confirmEncumberNextFiscalYear(invoice)) {
71                  return false;
72              }
73  
74              if (!confirmEncumberPriorFiscalYear(invoice)) {
75                  return false;
76              }
77          }
78          if (SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(invoice)) {
79              if (!confirmExpiredAccount(invoice)) {
80                  return false;
81              }
82          }
83  
84  
85          preRulesOK &= super.doPrompts(document);
86          return preRulesOK;
87      }
88  
89      /**
90       * Prompts user to confirm with a Yes or No to a question being asked.
91       *
92       * @param questionType    - type of question
93       * @param messageConstant - key to retrieve message
94       * @return - true if overriding, false otherwise
95       */
96      protected boolean askForConfirmation(String questionType, String messageConstant) {
97  
98          String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(messageConstant);
99          if (questionText.contains("{")) {
100             questionText = prepareQuestionText(questionType, questionText);
101         } else if (StringUtils.equals(messageConstant, OLEKeyConstants.ERROR_ACCOUNT_EXPIRED) || StringUtils.equals(messageConstant, PurapKeyConstants.WARNING_ITEM_TRADE_IN_AMOUNT_UNUSED)) {
102             questionText = questionType;
103         }
104 
105 
106         boolean confirmOverride = super.askOrAnalyzeYesNoQuestion(questionType, questionText);
107 
108         if (!confirmOverride) {
109             event.setActionForwardName(OLEConstants.MAPPING_BASIC);
110             return false;
111         }
112         return true;
113     }
114 
115     /**
116      * Creates the actual text of the question, replacing place holders like pay date threshold with an actual constant value.
117      *
118      * @param questionType - type of question
119      * @param questionText - actual text of question pulled from resource file
120      * @return - question text with place holders replaced
121      */
122     protected String prepareQuestionText(String questionType, String questionText) {
123         if (StringUtils.equals(questionType, PRQSDocumentsStrings.THRESHOLD_DAYS_OVERRIDE_QUESTION)) {
124             questionText = StringUtils.replace(questionText, "{0}", new Integer(PurapConstants.PRQS_PAY_DATE_DAYS_BEFORE_WARNING).toString());
125         }
126         return questionText;
127     }
128 
129     /**
130      * Validates if the pay date threshold has not been passed, if so confirmation is required by the user to
131      * exceed the threshold.
132      *
133      * @param invoice - invoice document
134      * @return - true if threshold has not been surpassed or if user confirmed ok to override, false otherwise
135      */
136     public boolean confirmPayDayNotOverThresholdDaysAway(InvoiceDocument invoice) {
137 
138         // If the pay date is more than the threshold number of days in the future, ask for confirmation.
139         //boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedPayDateNotOverThresholdDaysAwayEvent("", invoice));
140 
141         //if (!rulePassed) {
142         // The problem is that rulePassed always return true
143         int thresholdDays = PurapConstants.PRQS_PAY_DATE_DAYS_BEFORE_WARNING;
144         if ((invoice.getInvoicePayDate() != null) && SpringContext.getBean(PurapService.class).isDateMoreThanANumberOfDaysAway(invoice.getInvoicePayDate(), thresholdDays)) {
145             return askForConfirmation(PRQSDocumentsStrings.THRESHOLD_DAYS_OVERRIDE_QUESTION, PurapKeyConstants.MESSAGE_PAYMENT_REQUEST_PAYDATE_OVER_THRESHOLD_DAYS);
146         }
147         return true;
148     }
149 
150     public boolean confirmUnusedTradeIn(InvoiceDocument invoice) {
151         boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedTradeInWarningEvent("", invoice));
152 
153         if (!rulePassed) {
154             return askForConfirmation(PRQSDocumentsStrings.UNUSED_TRADE_IN_QUESTION, PurapKeyConstants.WARNING_ITEM_TRADE_IN_AMOUNT_UNUSED);
155         }
156         return true;
157     }
158 
159     public boolean confirmExpiredAccount(InvoiceDocument invoice) {
160         boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedExpiredAccountWarningEvent("", invoice));
161 
162         if (!rulePassed) {
163             return askForConfirmation(PRQSDocumentsStrings.EXPIRED_ACCOUNT_QUESTION, OLEKeyConstants.ERROR_ACCOUNT_EXPIRED);
164         }
165         return true;
166     }
167 
168     public boolean confirmEncumberNextFiscalYear(InvoiceDocument invoice) {
169         Integer fiscalYear = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
170         if (invoice.getPurchaseOrderDocument().getPostingYear().intValue() > fiscalYear) {
171             return askForConfirmation(PRQSDocumentsStrings.ENCUMBER_NEXT_FISCAL_YEAR_QUESTION, PurapKeyConstants.WARNING_ENCUMBER_NEXT_FY);
172         }
173 
174         return true;
175     }
176 
177     public boolean confirmEncumberPriorFiscalYear(InvoiceDocument invoice) {
178 
179         Integer fiscalYear = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
180         if (invoice.getPurchaseOrderDocument().getPostingYear().intValue() == fiscalYear && SpringContext.getBean(InvoiceService.class).allowBackpost(invoice)) {
181             return askForConfirmation(PRQSDocumentsStrings.ENCUMBER_PRIOR_FISCAL_YEAR_QUESTION, PurapKeyConstants.WARNING_ENCUMBER_PRIOR_FY);
182         }
183         return true;
184     }
185 
186     /**
187      * @see AccountsPayableDocumentPreRulesBase#getDocumentName()
188      */
189     @Override
190     public String getDocumentName() {
191         return "Invoice";
192     }
193 
194     /**
195      * @see AccountsPayableDocumentPreRulesBase#createInvoiceNoMatchQuestionText(org.kuali.ole.module.purap.document.AccountsPayableDocument)
196      */
197     @Override
198     public String createInvoiceNoMatchQuestionText(AccountsPayableDocument accountsPayableDocument) {
199 
200         String questionText = super.createInvoiceNoMatchQuestionText(accountsPayableDocument);
201 
202         CurrencyFormatter cf = new CurrencyFormatter();
203         InvoiceDocument invoice = (InvoiceDocument) accountsPayableDocument;
204 
205         StringBuffer questionTextBuffer = new StringBuffer("");
206         questionTextBuffer.append(questionText);
207 
208         questionTextBuffer.append("[br][br][b]Summary Detail Below:[b][br][br][table questionTable]");
209         questionTextBuffer.append("[tr][td leftTd]Vendor Invoice Amount entered on start screen:[/td][td rightTd]" + (String) cf.format(invoice.getInitialAmount()) + "[/td][/tr]");
210         questionTextBuffer.append("[tr][td leftTd]Invoice Total Prior to Additional Charges:[/td][td rightTd]" + (String) cf.format(invoice.getTotalPreTaxDollarAmountAboveLineItems()) + "[/td][/tr]");
211 
212 
213         //only add this line if invoice has a discount
214         if (invoice.isDiscount()) {
215             questionTextBuffer.append("[tr][td leftTd]Total Before Discount:[/td][td rightTd]" + (String) cf.format(invoice.getGrandPreTaxTotalExcludingDiscount()) + "[/td][/tr]");
216         }
217 
218         //if sales tax is enabled, show additional summary lines
219         boolean salesTaxInd = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND);
220         if (salesTaxInd) {
221             questionTextBuffer.append("[tr][td leftTd]Grand Total Prior to Tax:[/td][td rightTd]" + (String) cf.format(invoice.getGrandPreTaxTotal()) + "[/td][/tr]");
222             questionTextBuffer.append("[tr][td leftTd]Grand Total Tax:[/td][td rightTd]" + (String) cf.format(invoice.getGrandTaxAmount()) + "[/td][/tr]");
223         }
224 
225         questionTextBuffer.append("[tr][td leftTd]Grand Total:[/td][td rightTd]" + (String) cf.format(invoice.getGrandTotal()) + "[/td][/tr][/table]");
226 
227         return questionTextBuffer.toString();
228 
229     }
230 
231     @Override
232     protected boolean checkCAMSWarningStatus(PurchasingAccountsPayableDocument purapDocument) {
233         return PurapConstants.CAMSWarningStatuses.PAYMENT_REQUEST_STATUS_WARNING_NO_CAMS_DATA.contains(purapDocument.getApplicationDocumentStatus());
234     }
235 
236     /**
237      * Determines if the amount entered on the init tab is mismatched with the grand total of the document.
238      *
239      * @param accountsPayableDocument
240      * @return
241      */
242     @Override
243     protected boolean validateInvoiceTotalsAreMismatched(AccountsPayableDocument accountsPayableDocument) {
244         boolean mismatched = false;
245         InvoiceDocument payReqDoc = (InvoiceDocument) accountsPayableDocument;
246         String[] excludeArray = {PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE};
247 
248         //  if UseTax is included, then the invoiceInitialAmount should be compared against the 
249         // total amount NOT INCLUDING tax
250         if (payReqDoc.isUseTaxIndicator()) {
251             if (payReqDoc.getTotalPreTaxDollarAmountAllItems(excludeArray).compareTo(accountsPayableDocument.getInitialAmount()) != 0 && !accountsPayableDocument.isUnmatchedOverride()) {
252                 mismatched = true;
253             }
254         }
255 
256         //  if NO UseTax, then the invoiceInitialAmount should be compared against the 
257         // total amount INCLUDING sales tax (since if the vendor invoices with sales tax, then we pay it)
258         else {
259             if (accountsPayableDocument.getTotalDollarAmountAllItems(excludeArray).compareTo(accountsPayableDocument.getInitialAmount()) != 0 && !accountsPayableDocument.isUnmatchedOverride()) {
260                 mismatched = true;
261             }
262         }
263 
264         return mismatched;
265     }
266 
267 }