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.ar.document.validation.impl;
017
018 import java.util.HashMap;
019 import java.util.Map;
020
021 import org.apache.commons.lang.StringUtils;
022 import org.kuali.kfs.module.ar.ArConstants;
023 import org.kuali.kfs.module.ar.ArKeyConstants;
024 import org.kuali.kfs.module.ar.ArPropertyConstants;
025 import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail;
026 import org.kuali.kfs.module.ar.businessobject.OrganizationAccountingDefault;
027 import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument;
028 import org.kuali.kfs.module.ar.document.CustomerInvoiceWriteoffDocument;
029 import org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService;
030 import org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService;
031 import org.kuali.kfs.module.ar.document.validation.ContinueCustomerInvoiceWriteoffDocumentRule;
032 import org.kuali.kfs.sys.KFSConstants;
033 import org.kuali.kfs.sys.context.SpringContext;
034 import org.kuali.kfs.sys.service.UniversityDateService;
035 import org.kuali.rice.kns.document.Document;
036 import org.kuali.rice.kns.document.TransactionalDocument;
037 import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
038 import org.kuali.rice.kns.service.BusinessObjectService;
039 import org.kuali.rice.kns.service.DocumentService;
040 import org.kuali.rice.kns.service.ParameterService;
041 import org.kuali.rice.kns.util.GlobalVariables;
042 import org.kuali.rice.kns.util.KNSConstants;
043 import org.kuali.rice.kns.util.KualiDecimal;
044 import org.kuali.rice.kns.util.ObjectUtils;
045
046 public class CustomerInvoiceWriteoffDocumentRule extends TransactionalDocumentRuleBase implements ContinueCustomerInvoiceWriteoffDocumentRule<TransactionalDocument> {
047
048 /**
049 * @see org.kuali.rice.kns.rules.DocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.Document)
050 */
051 protected boolean processCustomSaveDocumentBusinessRules(Document document) {
052 boolean success = super.processCustomSaveDocumentBusinessRules(document);
053
054 GlobalVariables.getMessageMap().addToErrorPath(KNSConstants.DOCUMENT_PROPERTY_NAME);
055
056 CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument = (CustomerInvoiceWriteoffDocument) document;
057 success &= validateCustomerNote(customerInvoiceWriteoffDocument.getCustomerNote());
058 success &= validateWriteoffGLPEGenerationInformation(customerInvoiceWriteoffDocument);
059 GlobalVariables.getMessageMap().removeFromErrorPath(KNSConstants.DOCUMENT_PROPERTY_NAME);
060
061 return success;
062 }
063
064 @Override
065 protected boolean processCustomRouteDocumentBusinessRules(Document document) {
066 boolean success = super.processCustomSaveDocumentBusinessRules(document);
067
068 GlobalVariables.getMessageMap().addToErrorPath(KNSConstants.DOCUMENT_PROPERTY_NAME);
069
070 CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument = (CustomerInvoiceWriteoffDocument) document;
071 success &= validateCustomerNote(customerInvoiceWriteoffDocument.getCustomerNote());
072 success &= validateWriteoffGLPEGenerationInformation(customerInvoiceWriteoffDocument);
073 success &= doesCustomerInvoiceDocumentHaveValidBalance(customerInvoiceWriteoffDocument);
074
075 GlobalVariables.getMessageMap().removeFromErrorPath(KNSConstants.DOCUMENT_PROPERTY_NAME);
076
077 return success;
078 }
079
080 protected boolean validateCustomerNote(String customerNote) {
081 boolean success = true;
082 if (StringUtils.isNotEmpty(customerNote) && (customerNote.trim().length() < 5)) {
083 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_NOTE, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_CUSTOMER_NOTE_INVALID);
084 success = false;
085 }
086
087 return success;
088 }
089
090 /**
091 * This method validates any writeoff GLPE required information
092 *
093 * @param customerInvoiceWriteoffDocument
094 * @return
095 */
096 protected boolean validateWriteoffGLPEGenerationInformation(CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument) {
097 boolean success = true;
098
099 String writeoffGenerationOption = SpringContext.getBean(ParameterService.class).getParameterValue(CustomerInvoiceWriteoffDocument.class, ArConstants.GLPE_WRITEOFF_GENERATION_METHOD);
100
101 if (ArConstants.GLPE_WRITEOFF_GENERATION_METHOD_CHART.equals(writeoffGenerationOption)) {
102 for (CustomerInvoiceDetail customerInvoiceDetail : customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getCustomerInvoiceDetailsWithoutDiscounts()) {
103 success &= doesChartCodeHaveCorrespondingWriteoffObjectCode(customerInvoiceDetail);
104 }
105 }
106 else if (ArConstants.GLPE_WRITEOFF_GENERATION_METHOD_ORG_ACCT_DEFAULT.equals(writeoffGenerationOption)) {
107 success &= doesOrganizationAccountingDefaultHaveWriteoffInformation(customerInvoiceWriteoffDocument);
108 }
109
110 String writeoffTaxGenerationOption = SpringContext.getBean(ParameterService.class).getParameterValue(CustomerInvoiceWriteoffDocument.class, ArConstants.ALLOW_SALES_TAX_LIABILITY_ADJUSTMENT_IND);
111 if (ArConstants.ALLOW_SALES_TAX_LIABILITY_ADJUSTMENT_IND_NO.equals(writeoffTaxGenerationOption)) {
112 success &= doesOrganizationAccountingDefaultHaveWriteoffInformation(customerInvoiceWriteoffDocument);
113 }
114
115 return success;
116 }
117
118 /**
119 * This method checks if the chart object code using on the invoice detail has a corresponding
120 *
121 * @param customerInvoiceDetail
122 * @return TODO
123 */
124 protected boolean doesChartCodeHaveCorrespondingWriteoffObjectCode(CustomerInvoiceDetail customerInvoiceDetail) {
125 boolean success = true;
126
127 String writeoffObjectCode = SpringContext.getBean(ParameterService.class).getParameterValue(CustomerInvoiceWriteoffDocument.class, ArConstants.GLPE_WRITEOFF_OBJECT_CODE_BY_CHART, customerInvoiceDetail.getChartOfAccountsCode());
128 if (StringUtils.isBlank(writeoffObjectCode)) {
129 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerInvoiceWriteoffDocumentFields.CUSTOMER_INVOICE_DETAILS_FOR_WRITEOFF, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_CHART_WRITEOFF_OBJECT_DOESNT_EXIST, customerInvoiceDetail.getChartOfAccountsCode());
130 success = false;
131 }
132
133 return success;
134 }
135
136 protected boolean doesOrganizationAccountingDefaultHaveWriteoffInformation(CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument) {
137 boolean success = true;
138 Integer currentFiscalYear = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
139 String billByChartOfAccountCode = customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getBillByChartOfAccountCode();
140 String billedByOrganizationCode = customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getBilledByOrganizationCode();
141
142 Map<String, Object> criteria = new HashMap<String, Object>();
143 criteria.put("universityFiscalYear", currentFiscalYear);
144 criteria.put("chartOfAccountsCode", billByChartOfAccountCode);
145 criteria.put("organizationCode", billedByOrganizationCode);
146
147 OrganizationAccountingDefault organizationAccountingDefault = (OrganizationAccountingDefault) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(OrganizationAccountingDefault.class, criteria);
148
149 if (ObjectUtils.isNotNull(organizationAccountingDefault)) {
150 success &= doesWriteoffAccountNumberExist(organizationAccountingDefault);
151 success &= doesWriteoffChartOfAccountsCodeExist(organizationAccountingDefault);
152 success &= doesWriteoffFinancialObjectCodeExist(organizationAccountingDefault);
153 }
154 else {
155 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerInvoiceWriteoffDocumentFields.CUSTOMER_INVOICE_DETAILS_FOR_WRITEOFF, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_FAU_MUST_EXIST, new String[] { currentFiscalYear.toString(), billByChartOfAccountCode, billedByOrganizationCode });
156 success = false;
157 }
158
159 return success;
160
161 }
162
163 /**
164 * This method returns true if payment account number is provided and is valid.
165 *
166 * @param doc
167 * @return
168 */
169 protected boolean doesWriteoffAccountNumberExist(OrganizationAccountingDefault organizationAccountingDefault) {
170
171 if (StringUtils.isBlank(organizationAccountingDefault.getWriteoffAccountNumber())) {
172 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerInvoiceWriteoffDocumentFields.CUSTOMER_INVOICE_DETAILS_FOR_WRITEOFF, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_FAU_ACCOUNT_MUST_EXIST, new String[] { organizationAccountingDefault.getUniversityFiscalYear().toString(), organizationAccountingDefault.getChartOfAccountsCode(), organizationAccountingDefault.getOrganizationCode() });
173 return false;
174 }
175
176 return true;
177 }
178
179 /**
180 * This method returns true if payment chart of accounts code is provided and is valid
181 *
182 * @param doc
183 * @return
184 */
185 protected boolean doesWriteoffChartOfAccountsCodeExist(OrganizationAccountingDefault organizationAccountingDefault) {
186
187 if (StringUtils.isBlank(organizationAccountingDefault.getWriteoffChartOfAccountsCode())) {
188 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerInvoiceWriteoffDocumentFields.CUSTOMER_INVOICE_DETAILS_FOR_WRITEOFF, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_FAU_CHART_MUST_EXIST, new String[] { organizationAccountingDefault.getUniversityFiscalYear().toString(), organizationAccountingDefault.getChartOfAccountsCode(), organizationAccountingDefault.getOrganizationCode() });
189 return false;
190 }
191
192 return true;
193 }
194
195 /**
196 * This method returns true if payment financial object code is provided and is valid
197 *
198 * @param doc
199 * @return
200 */
201 protected boolean doesWriteoffFinancialObjectCodeExist(OrganizationAccountingDefault organizationAccountingDefault) {
202 if (StringUtils.isBlank(organizationAccountingDefault.getWriteoffFinancialObjectCode())) {
203 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerInvoiceWriteoffDocumentFields.CUSTOMER_INVOICE_DETAILS_FOR_WRITEOFF, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_FAU_OBJECT_CODE_MUST_EXIST, new String[] { organizationAccountingDefault.getUniversityFiscalYear().toString(), organizationAccountingDefault.getChartOfAccountsCode(), organizationAccountingDefault.getOrganizationCode() });
204 return false;
205 }
206
207 return true;
208 }
209
210 /**
211 * This method returns true if customer invoice document for writeoff does not have a credit balance (i.e. a open amount less
212 * than 0).
213 *
214 * @param customerInvoiceWriteoffDocument
215 * @return
216 */
217 protected boolean doesCustomerInvoiceDocumentHaveValidBalance(CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument) {
218 if (KualiDecimal.ZERO.isGreaterEqual(customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getOpenAmount())) {
219 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerInvoiceWriteoffDocumentFields.CUSTOMER_INVOICE_DETAILS_FOR_WRITEOFF, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_INVOICE_HAS_CREDIT_BALANCE);
220 return false;
221 }
222 return true;
223 }
224
225 /**
226 * This method checks if there is no another CRM in route for the invoice not in route if CRM status is one of the following:
227 * processed, cancelled, or disapproved
228 *
229 * @param invoice
230 * @return
231 */
232 public boolean checkIfThereIsNoAnotherCRMInRouteForTheInvoice(String invoiceDocumentNumber) {
233 CustomerInvoiceWriteoffDocumentService service = SpringContext.getBean(CustomerInvoiceWriteoffDocumentService.class);
234 boolean success = service.checkIfThereIsNoAnotherCRMInRouteForTheInvoice(invoiceDocumentNumber);
235 if (!success)
236 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT_ONE_CRM_IN_ROUTE_PER_INVOICE);
237
238 return success;
239 }
240
241 /**
242 * This method checks if there is no another writeoff in route for the invoice not in route if CRM status is one of the
243 * following: processed, cancelled, or disapproved
244 *
245 * @param invoice
246 * @return
247 */
248 public boolean checkIfThereIsNoAnotherWriteoffInRouteForTheInvoice(String invoiceDocumentNumber) {
249 CustomerInvoiceWriteoffDocumentService service = SpringContext.getBean(CustomerInvoiceWriteoffDocumentService.class);
250 boolean success = service.checkIfThereIsNoAnotherWriteoffInRouteForTheInvoice(invoiceDocumentNumber);
251 if (!success)
252 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_INVOICE_WRITEOFF_ONE_WRITEOFF_IN_ROUTE_PER_INVOICE);
253
254 return success;
255 }
256
257 /*
258 * @see org.kuali.kfs.module.ar.document.validation.ContinueCustomerInvoiceWriteoffDocumentRule#processContinueCustomerInvoiceWriteoffDocumentRules(org.kuali.kfs.sys.document.AccountingDocument)
259 */
260
261 public boolean processContinueCustomerInvoiceWriteoffDocumentRules(TransactionalDocument document) {
262 boolean success;
263 CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument = (CustomerInvoiceWriteoffDocument) document;
264
265 success = checkIfInvoiceNumberIsValid(customerInvoiceWriteoffDocument.getFinancialDocumentReferenceInvoiceNumber());
266 if (success)
267 success = doesCustomerInvoiceDocumentHaveValidBalance(customerInvoiceWriteoffDocument);
268 if (success)
269 success = checkIfThereIsNoAnotherCRMInRouteForTheInvoice(customerInvoiceWriteoffDocument.getFinancialDocumentReferenceInvoiceNumber());
270 if (success)
271 success = checkIfThereIsNoAnotherWriteoffInRouteForTheInvoice(customerInvoiceWriteoffDocument.getFinancialDocumentReferenceInvoiceNumber());
272
273 return success;
274 }
275
276 public boolean checkIfInvoiceNumberIsValid(String invDocumentNumber) {
277 boolean success = true;
278
279 if (ObjectUtils.isNull(invDocumentNumber) || StringUtils.isBlank(invDocumentNumber)) {
280 success = false;
281 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT__INVOICE_DOCUMENT_NUMBER_IS_REQUIRED);
282 }
283 else {
284 CustomerInvoiceDocumentService service = SpringContext.getBean(CustomerInvoiceDocumentService.class);
285 CustomerInvoiceDocument customerInvoiceDocument = service.getInvoiceByInvoiceDocumentNumber(invDocumentNumber);
286
287 if (ObjectUtils.isNull(customerInvoiceDocument)) {
288 success = false;
289 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT_INVALID_INVOICE_DOCUMENT_NUMBER);
290 }
291 else if (!SpringContext.getBean(CustomerInvoiceDocumentService.class).checkIfInvoiceNumberIsFinal(invDocumentNumber)) {
292 success = false;
293 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_INVOICE_DOCUMENT_NOT_FINAL);
294 }
295
296 }
297 return success;
298 }
299 }