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 }